📨 Mediator Pattern = Decoupled Architecture
Tight coupling is hard to maintain. Mediator pattern with DI decouples components and simplifies communication.
📝 Mediator Implementation
// Mediator Interface
public interface IMediator
{
Task Send(IRequest request);
}
// Request Handler
public interface IRequestHandler
where TRequest : IRequest
{
Task Handle(TRequest request, CancellationToken cancellationToken);
}
// Concrete Mediator
public class Mediator : IMediator
{
private readonly IServiceProvider _serviceProvider;
public Mediator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Send(IRequest request)
{
var handlerType = typeof(IRequestHandler<,>)
.MakeGenericType(request.GetType(), typeof(TResponse));
var handler = _serviceProvider.GetRequiredService(handlerType);
var method = handlerType.GetMethod("Handle");
var result = await (Task)method.Invoke(handler,
new object[] { request, CancellationToken.None });
return result;
}
}
// Command Pattern
public class CreateOrderCommand : IRequest
{
public string CustomerId { get; set; }
public List Items { get; set; }
}
public class CreateOrderHandler : IRequestHandler
{
private readonly IOrderRepository _orderRepo;
private readonly ILogger _logger;
public CreateOrderHandler(IOrderRepository orderRepo, ILogger logger)
{
_orderRepo = orderRepo;
_logger = logger;
}
public async Task Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var order = new Order { CustomerId = request.CustomerId, Items = request.Items };
await _orderRepo.AddAsync(order);
_logger.LogInformation("Order created: {OrderId}", order.Id);
return order.Id;
}
}
🎯 Mediator with DI
// Service Registration
public static class MediatorExtensions
{
public static IServiceCollection AddMediator(this IServiceCollection services)
{
services.AddSingleton();
// Register all handlers
services.Scan(scan => scan
.FromAssembliesOf>()
.AddClasses(classes => classes.AssignableTo(typeof(IRequestHandler<,>)))
.AsImplementedInterfaces()
.WithScopedLifetime());
return services;
}
}
// Usage in Controller
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IMediator _mediator;
public OrdersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task> CreateOrder(CreateOrderCommand command)
{
var orderId = await _mediator.Send(command);
return CreatedAtAction(nameof(GetOrder), new { id = orderId }, orderId);
}
}
// Pipeline Behaviors (Middleware)
public class LoggingBehavior : IPipelineBehavior
{
private readonly ILogger> _logger;
public LoggingBehavior(ILogger> logger)
{
_logger = logger;
}
public async Task Handle(TRequest request,
RequestHandlerDelegate next,
CancellationToken cancellationToken)
{
_logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
var response = await next();
_logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
return response;
}
}
💡 Mediator Pattern Tips
- Use Mediator for decoupling
- Implement pipeline behaviors
- Use CQRS with Mediator
- Register handlers with DI
- Keep handlers focused
Mediator pattern with DI creates clean, maintainable architectures. It’s essential for large applications.
