Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup .NET9
uses: actions/setup-dotnet@v4
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 9.x
dotnet-version: 10.x

- name: Restore dependencies
run: dotnet restore
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup .NET9
uses: actions/setup-dotnet@v4
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 9.x
dotnet-version: 10.x

- name: Restore dependencies
run: dotnet restore
Expand Down
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

Nothing yet.
### Added

- `EventBus.GetHandlersAsync` method.
- `EventBus` unit tests.

### Changed

- Improved `EventBus` implementation.

### Fixed

- GitHub Actions.
- LICENSE Year.
- NuGet upgrades.

## [10.0.0] - 2025-11-29

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Logitar.Security" Version="10.0.0" />
<PackageReference Include="Logitar.Security" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0">
<PrivateAssets>all</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Authors>Francis Pion</Authors>
<Product>Logitar.NET</Product>
<Description>Provides an implementation of a relational event store to be used with the Event Sourcing architecture pattern, Entity Framework Core and PostgreSQL.</Description>
<Copyright>© 2024 Logitar All Rights Reserved.</Copyright>
<Copyright>© 2025 Logitar All Rights Reserved.</Copyright>
<PackageIcon>logitar.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
Expand Down Expand Up @@ -39,7 +39,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Logitar.Data.PostgreSQL" Version="10.0.0" />
<PackageReference Include="Logitar.Data.PostgreSQL" Version="10.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Authors>Francis Pion</Authors>
<Product>Logitar.NET</Product>
<Description>Provides an abstraction of a relational event store to be used with the Event Sourcing architecture pattern and Entity Framework Core.</Description>
<Copyright>© 2024 Logitar All Rights Reserved.</Copyright>
<Copyright>© 2025 Logitar All Rights Reserved.</Copyright>
<PackageIcon>logitar.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
Expand Down Expand Up @@ -39,7 +39,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Logitar.Data" Version="10.0.0" />
<PackageReference Include="Logitar.Data" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Authors>Francis Pion</Authors>
<Product>Logitar.NET</Product>
<Description>Provides an implementation of a relational event store to be used with the Event Sourcing architecture pattern, Entity Framework Core and Microsoft SQL Server.</Description>
<Copyright>© 2024 Logitar All Rights Reserved.</Copyright>
<Copyright>© 2025 Logitar All Rights Reserved.</Copyright>
<PackageIcon>logitar.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
Expand Down Expand Up @@ -39,7 +39,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Logitar.Data.SqlServer" Version="10.0.0" />
<PackageReference Include="Logitar.Data.SqlServer" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
</ItemGroup>

Expand Down
44 changes: 33 additions & 11 deletions lib/Logitar.EventSourcing.Infrastructure/EventBus.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace Logitar.EventSourcing.Infrastructure;

Expand All @@ -8,10 +7,15 @@ namespace Logitar.EventSourcing.Infrastructure;
/// </summary>
public class EventBus : IEventBus
{
/// <summary>
/// The name of the handler method.
/// </summary>
protected const string HandlerName = nameof(IEventHandler<>.HandleAsync);

/// <summary>
/// Gets the service provider.
/// </summary>
protected IServiceProvider ServiceProvider { get; }
protected virtual IServiceProvider ServiceProvider { get; }

/// <summary>
/// Initializes a new instance of the <see cref="EventBus"/> class.
Expand All @@ -30,22 +34,40 @@ public EventBus(IServiceProvider serviceProvider)
/// <returns>The asynchronous operation.</returns>
public async Task PublishAsync(IEvent @event, CancellationToken cancellationToken)
{
IEnumerable<object?> handlers = ServiceProvider.GetServices(typeof(IEventHandler<>).MakeGenericType(@event.GetType()));
if (handlers.Any())
IReadOnlyCollection<object> handlers = await GetHandlersAsync(@event, cancellationToken);
if (handlers.Count > 0)
{
Type[] parameterTypes = [@event.GetType(), typeof(CancellationToken)];
object[] parameters = [@event, cancellationToken];
foreach (object? handler in handlers)
foreach (object handler in handlers)
{
if (handler is not null)
Type handlerType = handler.GetType();
MethodInfo handle = handler.GetType().GetMethod(HandlerName, parameterTypes)
?? throw new InvalidOperationException($"The handler {handlerType} must define a '{HandlerName}' method.");
if (handle.Invoke(handler, parameters) is Task task)
{
MethodInfo? handle = handler.GetType().GetMethod(nameof(IEventHandler<>.HandleAsync), parameterTypes);
if (handle is not null)
{
await (Task)handle.Invoke(handler, parameters)!;
}
await task;
}
else
{
throw new InvalidOperationException($"The handler {handlerType} {HandlerName} method must return a {nameof(Task)}.");
}
}
}
}

/// <summary>
/// Finds the handlers of the specified event.
/// </summary>
/// <param name="event">The event.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The event handlers.</returns>
protected virtual async Task<IReadOnlyCollection<object>> GetHandlersAsync(IEvent @event, CancellationToken cancellationToken)
{
return ServiceProvider.GetServices(typeof(IEventHandler<>).MakeGenericType(@event.GetType()))
.Where(handler => handler is not null)
.Select(handler => handler!)
.ToList()
.AsReadOnly();
}
}
2 changes: 1 addition & 1 deletion lib/Logitar.EventSourcing.Infrastructure/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Authors>Francis Pion</Authors>
<Product>Logitar.NET</Product>
<Description>Provides an abstraction of an event store to be used with the Event Sourcing architecture pattern.</Description>
<Copyright>© 2024 Logitar All Rights Reserved.</Copyright>
<Copyright>© 2025 Logitar All Rights Reserved.</Copyright>
<PackageIcon>logitar.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
Expand Down Expand Up @@ -39,6 +39,7 @@
</ItemGroup>

<ItemGroup>
<Using Include="System.Reflection" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion lib/Logitar.EventSourcing.Kurrent/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Authors>Francis Pion</Authors>
<Product>Logitar.NET</Product>
<Description>Provides an implementation of an event store to be used with the Event Sourcing architecture pattern, and EventStoreDB/Kurrent.</Description>
<Copyright>© 2024 Logitar All Rights Reserved.</Copyright>
<Copyright>© 2025 Logitar All Rights Reserved.</Copyright>
<PackageIcon>logitar.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
Expand Down
2 changes: 1 addition & 1 deletion lib/Logitar.EventSourcing/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Logitar
Copyright (c) 2025 Logitar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions lib/Logitar.EventSourcing/Logitar.EventSourcing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Authors>Francis Pion</Authors>
<Product>Logitar.NET</Product>
<Description>Provides an implementation of the Event Sourcing architecture pattern.</Description>
<Copyright>© 2024 Logitar All Rights Reserved.</Copyright>
<Copyright>© 2025 Logitar All Rights Reserved.</Copyright>
<PackageIcon>logitar.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
Expand All @@ -35,7 +35,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Logitar" Version="10.0.0" />
<PackageReference Include="Logitar" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.Extensions.DependencyInjection;
using Moq;

namespace Logitar.EventSourcing.Infrastructure;

[Trait(Traits.Category, Categories.Unit)]
public class EventBusTests
{
private readonly Mock<IEventHandler<UserGenderChanged>> _userGenderChangedHandler = new();
private readonly Mock<IEventHandler<UserLocaleChanged>> _userLocaleChangedHandler1 = new();
private readonly Mock<IEventHandler<UserLocaleChanged>> _userLocaleChangedHandler2 = new();

public EventBusTests()
{
}

[Fact(DisplayName = "PublishAsync: it should call all found event handlers.")]
public async Task Given_Handlers_When_PublishAsync_Then_AllCalled()
{
IServiceProvider serviceProvider = new ServiceCollection()
.AddSingleton(_userGenderChangedHandler.Object)
.AddSingleton(_userLocaleChangedHandler1.Object)
.AddSingleton(_userLocaleChangedHandler2.Object)
.BuildServiceProvider();
EventBus eventBus = new(serviceProvider);

UserLocaleChanged changed = new(CultureInfo.GetCultureInfo("fr-CA"));
CancellationToken cancellationToken = default;

await eventBus.PublishAsync(changed, cancellationToken);

_userLocaleChangedHandler1.Verify(x => x.HandleAsync(changed, cancellationToken), Times.Once);
_userLocaleChangedHandler2.Verify(x => x.HandleAsync(changed, cancellationToken), Times.Once);
}

[Fact(DisplayName = "PublishAsync: it should not call any handler when there are none.")]
public async Task Given_NoHandler_When_PublishAsync_Then_NoneCalled()
{
IServiceProvider serviceProvider = new ServiceCollection()
.AddSingleton(_userGenderChangedHandler.Object)
.BuildServiceProvider();
EventBus eventBus = new(serviceProvider);

UserLocaleChanged changed = new(CultureInfo.GetCultureInfo("fr-CA"));
CancellationToken cancellationToken = default;

await eventBus.PublishAsync(changed, cancellationToken);

_userGenderChangedHandler.Verify(x => x.HandleAsync(It.IsAny<UserGenderChanged>(), It.IsAny<CancellationToken>()), Times.Never);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Logitar.EventSourcing.Infrastructure;

internal enum Gender
public enum Gender
{
Female,
Male,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Logitar.EventSourcing.Infrastructure;

internal record UserGenderChanged(Gender? Gender) : DomainEvent;
public record UserGenderChanged(Gender? Gender) : DomainEvent;
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Logitar.EventSourcing.Infrastructure;

internal record UserLocaleChanged(CultureInfo Locale) : IEvent;
public record UserLocaleChanged(CultureInfo Locale) : IEvent;