TechnicalDogsbody.MicroMediator
1.0.0
See the version list below for details.
dotnet add package TechnicalDogsbody.MicroMediator --version 1.0.0
NuGet\Install-Package TechnicalDogsbody.MicroMediator -Version 1.0.0
<PackageReference Include="TechnicalDogsbody.MicroMediator" Version="1.0.0" />
<PackageVersion Include="TechnicalDogsbody.MicroMediator" Version="1.0.0" />
<PackageReference Include="TechnicalDogsbody.MicroMediator" />
paket add TechnicalDogsbody.MicroMediator --version 1.0.0
#r "nuget: TechnicalDogsbody.MicroMediator, 1.0.0"
#:package TechnicalDogsbody.MicroMediator@1.0.0
#addin nuget:?package=TechnicalDogsbody.MicroMediator&version=1.0.0
#tool nuget:?package=TechnicalDogsbody.MicroMediator&version=1.0.0
TechnicalDogsbody.MicroMediator
A lightweight, high-performance mediator pattern implementation for .NET with built-in validation, logging, and caching pipeline behaviours. Zero commercial licensing concerns.
Why MicroMediator?
MicroMediator outperforms MediatR across all metrics whilst maintaining a cleaner API and zero licensing costs.
Performance Comparison vs MediatR 11
| Benchmark | MicroMediator | MediatR | Advantage |
|---|---|---|---|
| Basic Send | 21 ns | 51 ns | 2.4x faster |
| Cold Start | 95 μs | 396 μs | 4.2x faster |
| Pipeline (Validation) | 380 ns | 867 ns | 2.3x faster |
| Throughput (10k sequential) | 151 μs | 446 μs | 2.9x faster |
| Throughput (10k parallel) | 287 μs | 617 μs | 2.1x faster |
Memory Efficiency
| Scenario | MicroMediator | MediatR | Savings |
|---|---|---|---|
| Single Request | 96 B | 296 B | 3.1x less |
| 100 Requests | 2.41 KB | 21.59 KB | 9x less |
| 10,000 Requests | 234 KB | 2,187 KB | 9.3x less |
Key Features
- 2-4x faster than MediatR across all scenarios
- 3-10x less memory allocation
- Zero commercial licensing costs (MediatR 12+ requires paid licence)
- Fluent builder API for clean, readable configuration
- Built-in behaviours: Validation, Logging, Caching
- ValueTask optimisation for zero-allocation fast paths
- AOT-compatible with minimal reflection (registration only)
- Cold start optimised (4.2x faster than MediatR)
Installation
dotnet add package TechnicalDogsbody.MicroMediator
Quick Start
1. Define Your Request and Handler
// Query (read operation)
public record GetProductByIdQuery : IRequest<Product?>
{
public int Id { get; init; }
}
public class GetProductByIdQueryHandler : IRequestHandler<GetProductByIdQuery, Product?>
{
public ValueTask<Product?> HandleAsync(GetProductByIdQuery request, CancellationToken cancellationToken)
{
// Your logic here
var product = _repository.GetById(request.Id);
return ValueTask.FromResult(product);
}
}
// Command (write operation)
public record CreateOrderCommand : IRequest<OrderResult>
{
public required string CustomerEmail { get; init; }
public List<OrderItem> Items { get; init; } = [];
}
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
public async ValueTask<OrderResult> HandleAsync(CreateOrderCommand request, CancellationToken cancellationToken)
{
// Your logic here
var orderId = await _orderService.CreateOrderAsync(request);
return OrderResult.Success(orderId);
}
}
2. Register with Dependency Injection
builder.Services
.AddMediator()
.AddHandler<GetProductByIdQueryHandler>()
.AddHandler<CreateOrderCommandHandler>()
.AddDefaultLoggingPipeline()
.AddDefaultCachingPipeline();
3. Use in Your Code
public class ProductController : ControllerBase
{
private readonly IMediator _mediator;
public ProductController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
var query = new GetProductByIdQuery { Id = id };
var product = await _mediator.SendAsync(query);
return product is not null
? Ok(product)
: NotFound();
}
[HttpPost]
public async Task<IActionResult> CreateOrder(CreateOrderCommand command)
{
var result = await _mediator.SendAsync(command);
return result.Success
? Created($"/orders/{result.OrderId}", result)
: BadRequest(result);
}
}
Advanced Features
Fluent Configuration
builder.Services
.AddMediator()
// Register handlers (discovers interfaces automatically)
.AddHandler<GetProductByIdQueryHandler>()
.AddHandler<SearchProductsQueryHandler>()
.AddHandler<CreateOrderCommandHandler>()
// Register validators (automatically adds ValidationBehavior)
.AddValidator<CreateOrderCommandValidator>()
.AddValidator<UpdateProductCommandValidator>()
// Add built-in pipeline behaviours
.AddDefaultLoggingPipeline()
.AddDefaultCachingPipeline()
// Add custom pipeline behaviours
.AddBehavior(typeof(PerformanceMonitoringBehavior<,>))
.AddBehavior(typeof(AuditBehavior<,>))
.AddBehavior(typeof(RetryBehavior<,>));
Request Caching
Implement ICacheableRequest on your queries for automatic caching:
public record GetProductByIdQuery : IRequest<Product?>, ICacheableRequest
{
public int Id { get; init; }
public string CacheKey => $"product-{Id}";
public TimeSpan? CacheDuration => TimeSpan.FromMinutes(10);
}
When you call AddDefaultCachingPipeline(), the CachingBehavior automatically caches responses for requests implementing ICacheableRequest.
FluentValidation Integration
Add validators and MicroMediator automatically wires up validation:
public class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand>
{
public CreateOrderCommandValidator()
{
RuleFor(x => x.CustomerEmail)
.NotEmpty()
.EmailAddress();
RuleFor(x => x.Items)
.NotEmpty()
.Must(items => items.Count <= 50);
}
}
// Registration
builder.Services
.AddMediator()
.AddHandler<CreateOrderCommandHandler>()
.AddValidator<CreateOrderCommandValidator>(); // Automatically adds ValidationBehavior
Custom Pipeline Behaviours
Create custom behaviours by implementing IPipelineBehavior<TRequest, TResponse>:
public class PerformanceMonitoringBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<PerformanceMonitoringBehavior<TRequest, TResponse>> _logger;
public PerformanceMonitoringBehavior(ILogger<PerformanceMonitoringBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async ValueTask<TResponse> HandleAsync(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
var stopwatch = Stopwatch.StartNew();
try
{
return await next();
}
finally
{
stopwatch.Stop();
if (stopwatch.ElapsedMilliseconds > 500)
{
_logger.LogWarning(
"Slow request: {RequestName} took {ElapsedMs}ms",
typeof(TRequest).Name,
stopwatch.ElapsedMilliseconds);
}
}
}
}
// Register it
builder.Services
.AddMediator()
.AddBehavior(typeof(PerformanceMonitoringBehavior<,>));
Pipeline Execution Order
Behaviours execute in reverse order of registration (last registered runs first):
builder.Services
.AddMediator()
.AddHandler<MyHandler>()
.AddValidator<MyValidator>() // 3. Validates (innermost)
.AddDefaultLoggingPipeline() // 2. Logs
.AddBehavior(typeof(RetryBehavior<,>)); // 1. Retries (outermost)
Request flow: RetryBehavior → LoggingBehavior → ValidationBehavior → Handler
Explicit Registration (Zero Reflection)
For maximum AOT compatibility and performance, use explicit type parameters:
builder.Services
.AddMediator()
.AddHandler<GetProductQuery, Product, GetProductQueryHandler>()
.AddValidator<CreateOrderCommand, CreateOrderCommandValidator>();
Use Cases
Perfect For
- Serverless/Azure Functions - 4.2x faster cold starts
- High-throughput APIs - 2-3x faster request processing
- Memory-constrained environments - 9x less allocation
- CQRS implementations - Clean separation of commands and queries
- Projects avoiding commercial licences - MediatR 12+ requires payment
When to Use MediatR Instead
- You need notification/event broadcasting (MicroMediator focuses on request/response)
- You're already using MediatR 11 and don't want to migrate
- You need streaming request support
Architecture
MicroMediator uses a wrapper pattern with dynamic dispatch and aggressive caching:
- Static generic caching - Each response type gets its own dictionary
- ConcurrentDictionary - Lock-free reads after first request
- Wrapper instances - Created once per request type
- Handler/behavior caching - Eliminates DI lookups on hot path
- ValueTask - Zero-allocation synchronous completions
Examples
The TechnicalDogsbody.MicroMediator.Examples project demonstrates:
- CQRS pattern with queries and commands
- FluentValidation integration
- Request caching
- Custom pipeline behaviours (performance monitoring, audit trail, retry logic)
- Structured logging
- Complete Web API implementation
Benchmarks
Run comprehensive benchmarks comparing MicroMediator to MediatR:
cd benchmarks
dotnet run -c Release
Includes:
- Basic send performance
- Pipeline overhead with validation
- Caching performance (hit/miss)
- Cold start performance
- Throughput at scale (100/1,000/10,000 requests)
Requirements
- .NET 8.0 or later
- FluentValidation 11.10.0+ (optional, for validation support)
Licence
MIT
Contributing
Contributions welcome! Please open an issue before submitting large changes.
Acknowledgements
Inspired by Jimmy Bogard's MediatR. Built from scratch with performance in mind.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FluentValidation (>= 11.10.0)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.