Ultra-lightweight, zero-reflection mediator for .NET with blazing performance.
BrilliantMediator is a high-performance implementation of the Mediator pattern that focuses on simplicity and speed.
- ⚡ Zero Reflection - Uses compiled generics, no
typeof()lookups at runtime - 🚀 Blazing Fast - Overhead < 50ns per operation
- 🎯 Type-Safe - Full compile-time checking via generics
- 📦 Tiny - ~100 lines of core code
- 🔧 Simple - Easy to understand and maintain
- 🌐 Framework Agnostic - Works with any .NET host (Console, Worker Service, ASP.NET Core)
- 📋 CQRS + Events - Commands, Queries, and parallel Events out of the box
- 🛠️ Source Generator - Zero-reflection handler registration generated at compile time
| Package | Description |
|---|---|
BrilliantMediator |
Core library |
BrilliantMediator.SourceGenerator |
Roslyn generator — registers handlers at compile time |
dotnet add package BrilliantMediator
# Optional: auto-register handlers at compile time
dotnet add package BrilliantMediator.SourceGeneratorusing Monbsoft.BrilliantMediator.Abstractions.Commands;
public class CreateUserCommand : ICommand<CreateUserResult>
{
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
public class CreateUserResult
{
public Guid UserId { get; set; }
public bool Success { get; set; }
}using Monbsoft.BrilliantMediator.Abstractions.Commands;
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, CreateUserResult>
{
private readonly IUserRepository _repository;
public CreateUserCommandHandler(IUserRepository repository)
{
_repository = repository;
}
public async Task<CreateUserResult> Handle(CreateUserCommand command, CancellationToken cancellationToken = default)
{
var user = new User { Id = Guid.NewGuid(), Name = command.Name, Email = command.Email };
await _repository.AddAsync(user, cancellationToken);
return new CreateUserResult { UserId = user.Id, Success = true };
}
}using Monbsoft.BrilliantMediator.Extensions;
// Startup / Program.cs
var services = new ServiceCollection();
services
.AddBrilliantMediator()
.AddCommandHandler<CreateUserCommand, CreateUserResult, CreateUserCommandHandler>()
// chain other handlers...
.Build();
var serviceProvider = services.BuildServiceProvider();
serviceProvider.UseBrilliantMediator(); // initialize handler registry
// Usage
var mediator = serviceProvider.GetRequiredService<IMediator>();
var result = await mediator.DispatchAsync<CreateUserCommand, CreateUserResult>(
new CreateUserCommand { Name = "John", Email = "john@example.com" }
);Commands represent actions that modify state.
public class SendEmailCommand : ICommand
{
public string To { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
}
public class SendEmailCommandHandler : ICommandHandler<SendEmailCommand>
{
public async Task Handle(SendEmailCommand command, CancellationToken cancellationToken = default)
{
// Send email...
await Task.CompletedTask;
}
}
// Usage
await mediator.DispatchAsync(new SendEmailCommand { To = "user@example.com", Subject = "Hello" });public class CalculateCommand : ICommand<int>
{
public int A { get; set; }
public int B { get; set; }
}
public class CalculateCommandHandler : ICommandHandler<CalculateCommand, int>
{
public Task<int> Handle(CalculateCommand command, CancellationToken cancellationToken = default)
{
return Task.FromResult(command.A + command.B);
}
}
// Usage
var result = await mediator.DispatchAsync<CalculateCommand, int>(
new CalculateCommand { A = 5, B = 3 }
);
Console.WriteLine(result); // 8Queries represent read operations that don't modify state.
using Monbsoft.BrilliantMediator.Abstractions.Queries;
public class GetUserQuery : IQuery<UserDto>
{
public Guid UserId { get; set; }
}
public class GetUserQueryHandler : IQueryHandler<GetUserQuery, UserDto>
{
private readonly IUserRepository _repository;
public GetUserQueryHandler(IUserRepository repository)
{
_repository = repository;
}
public async Task<UserDto> Handle(GetUserQuery query, CancellationToken cancellationToken = default)
{
var user = await _repository.GetByIdAsync(query.UserId, cancellationToken);
return new UserDto { Id = user.Id, Name = user.Name, Email = user.Email };
}
}
// Usage
var userDto = await mediator.SendAsync<GetUserQuery, UserDto>(
new GetUserQuery { UserId = userId }
);Multiple handlers can be registered for the same event — they run in parallel.
using Monbsoft.BrilliantMediator.Abstractions.Events;
public class UserCreatedEvent : IEvent
{
public Guid UserId { get; set; }
public string Email { get; set; } = string.Empty;
}
public class SendWelcomeEmailHandler : IEventHandler<UserCreatedEvent>
{
public async Task Handle(UserCreatedEvent @event, CancellationToken cancellationToken = default)
{
// Send welcome email...
await Task.CompletedTask;
}
}
public class AuditUserCreationHandler : IEventHandler<UserCreatedEvent>
{
public async Task Handle(UserCreatedEvent @event, CancellationToken cancellationToken = default)
{
// Log audit entry...
await Task.CompletedTask;
}
}
// Publish — both handlers run in parallel
await mediator.PublishAsync(new UserCreatedEvent { UserId = userId, Email = "user@example.com" });services
.AddBrilliantMediator()
.AddCommandHandler<CreateUserCommand, UserDto, CreateUserCommandHandler>()
.AddCommandHandler<DeleteUserCommand, DeleteUserCommandHandler>()
.AddQueryHandler<GetUserQuery, UserDto, GetUserQueryHandler>()
.AddEventHandler<UserCreatedEvent, SendWelcomeEmailHandler>()
.AddEventHandler<UserCreatedEvent, AuditUserCreationHandler>()
.Build();
var serviceProvider = services.BuildServiceProvider();
serviceProvider.UseBrilliantMediator();Add the BrilliantMediator.SourceGenerator package to your .csproj:
<PackageReference Include="BrilliantMediator.SourceGenerator"
Version="3.0.0"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />The generator scans your assembly at compile time and generates an AddGeneratedHandlers() extension method. Use it in place of manual registrations:
services
.AddBrilliantMediator()
.AddGeneratedHandlers() // generated by BrilliantMediator.SourceGenerator
.Build();
var serviceProvider = services.BuildServiceProvider();
serviceProvider.UseBrilliantMediator();To scan handlers from additional assemblies, add the attribute:
[assembly: BrilliantMediatorGenerator(
Namespace = "MyApp.Infrastructure.Generated",
Assemblies = [typeof(MyCommandHandler), typeof(MyQueryHandler)])]The current assembly is always scanned. Assemblies lets you include handlers from other referenced assemblies.
services
.AddBrilliantMediator()
.AddCommandHandler<MyCommand, MyCommandHandler>(ServiceLifetime.Singleton)
.AddQueryHandler<MyQuery, MyResult, MyQueryHandler>(ServiceLifetime.Transient)
.Build();Default lifetime is Scoped.
Works without any coupling to IApplicationBuilder. Call UseBrilliantMediator() on app.Services:
var app = builder.Build();
app.Services.UseBrilliantMediator();- No Reflection at runtime — handler types are stored in a
ConcurrentDictionarypopulated at startup - DI scope per call — each dispatch creates a dedicated
IServiceScope, ensuring proper scoped service lifetime (e.g.,DbContext) ImmutableListfor events — lock-free reads for parallel event dispatch- Compile-time type safety — all generics resolved by the JIT, no dynamic dispatch
BrilliantMediator throws HandlerNotRegisteredException when a handler is not registered:
try
{
await mediator.DispatchAsync(new SomeCommand());
}
catch (HandlerNotRegisteredException ex)
{
Console.WriteLine(ex.Message);
// "No handler registered for command 'SomeCommand'"
}// Dispatch command without response
Task DispatchAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : ICommand;
// Dispatch command with response
Task<TResponse> DispatchAsync<TCommand, TResponse>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : ICommand<TResponse>;
// Send query
Task<TResponse> SendAsync<TQuery, TResponse>(TQuery query, CancellationToken cancellationToken = default)
where TQuery : IQuery<TResponse>;
// Publish event (parallel handlers)
Task PublishAsync<TEvent>(TEvent @event, CancellationToken cancellationToken = default)
where TEvent : IEvent;void RegisterCommandHandler<TCommand>() where TCommand : ICommand;
void RegisterCommandHandler<TCommand, TResponse>() where TCommand : ICommand<TResponse>;
void RegisterQueryHandler<TQuery, TResponse>() where TQuery : IQuery<TResponse>;
void RegisterEventHandler<TEvent>() where TEvent : IEvent;Note:
IHandlerRegistryis called internally byUseBrilliantMediator(). Application code should only depend onIMediator.
- .NET 10.0
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.