Mediator Pattern
Decoupling senders from handlers via a central dispatcher — MediatR in practice.
Mediator removes direct dependencies between components by routing communication through a central
object. In .NET, this almost always means MediatR: instead of a controller directly calling
five different services, it sends one IRequest through IMediator.Send(), and a single matching
IRequestHandler processes it — the controller doesn't know or care what
handles it.
public record GetOrderQuery(Guid OrderId) : IRequest<OrderDto>;
public class GetOrderQueryHandler(IOrderRepository repo) : IRequestHandler<GetOrderQuery, OrderDto>
{
public async Task<OrderDto> Handle(GetOrderQuery request, CancellationToken ct)
{
var order = await repo.GetByIdAsync(request.OrderId)
?? throw new NotFoundException(request.OrderId);
return order.ToDto();
}
}
// controller
[HttpGet("{id}")]
public async Task<OrderDto> Get(Guid id) => await mediator.Send(new GetOrderQuery(id));
This is also the natural on-ramp into CQRS (next topic): Mediator gives you a clean, consistent
way to route both Commands and Queries through the same pipeline, and MediatR's pipeline behaviors
(IPipelineBehavior) are where cross-cutting concerns — validation, logging,
transactions — get bolted on without cluttering individual handlers.