.NET :
Alternativas ao MediatR (que vai deixar de ser grátis)
![]() |
Neste artigo veremos algumas alternativas para substituir o MediatR que em breve não será mais gratuito. (Veja a série de vídeos no meu canal do Youtube) |
Conforme post do Jimmy Bogard o criador do AutoMapper e do MediatR publicado neste link , ele anunciou que em breve essas bibliotecas deixariam de ser gratuitas.
Assim, se você usa o AutoMapper e o Mediator
em seus projetos, e quer economizar, deve procurar alternativas
gratuitas a ambos os recursos.
A seguir vou apresentar a seguir algumas alterantivas open source (ainda) considerando a implementação do padrão CQRS onde é muito comum usar o MediatR.
Nota: Para ver as alternativas ao AutoMapper veja o meu vídeo neste link (meu canal no YouTube)
Alternativas Open Source ao MediatR
1. Mediator (Microsoft)
Pacote: Microsoft.Extensions.MediatR,
implementação leve e oficial da Microsoft similar em conceito ao
MediatR original.
// Instale o pacote // dotnet add package Microsoft.Extensions.MediatR // Configuração (similar ao MediatR) services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Startup>()); // Uso em controllers permanece similar public class MyController : ControllerBase { private readonly IMediator _mediator; public MyController(IMediator mediator) { _mediator = mediator; } [HttpGet] public async Task<IActionResult> Get() { var result = await _mediator.Send(new MyQuery()); return Ok(result); } } |
2.
TinyMediator
Pacote: TinyMediator , alternativa minimalista e de alto
desempenho
Instalação : dotnet add package TinyMediator
Configuração básica:
// No seu Program.cs ou Startup.cs builder.Services.AddTinyMediator(options => { // Opcional: Configurar assemblies para scan options.ScanAssemblies(typeof(Program).Assembly); }); |
Definindo Command/Queries
// Command com resposta public record CreateProductCommand(string Name, decimal Price) : IRequest<ProductResponse>; // Query public record GetProductByIdQuery(int Id) : IRequest<ProductResponse>; // Command sem resposta public record LogProductActionCommand(string Action) : IRequest; |
Implementando Handlers
// Handler com resposta public class CreateProductHandler : IRequestHandler<CreateProductCommand, ProductResponse> { public async Task<ProductResponse> Handle(CreateProductCommand request, CancellationToken cancellationToken) { // Lógica de criação do produto return new ProductResponse(Guid.NewGuid(), request.Name, request.Price); } } // Handler sem resposta public class LogProductActionHandler : IRequestHandler<LogProductActionCommand> { public async Task Handle(LogProductActionCommand request, CancellationToken cancellationToken) { Console.WriteLine($"Action logged: {request.Action}"); } } |
Uso em Controllers ou Services
[ApiController] [Route("[controller]")] public class ProductsController : ControllerBase { private readonly ISender _mediator; public ProductsController(ISender mediator) { _mediator = mediator; } [HttpPost] public async Task<IActionResult> Create(CreateProductCommand command) { var result = await _mediator.Send(command); return Ok(result); } [HttpGet("{id}")] public async Task<IActionResult> Get(int id) { var result = await _mediator.Send(new GetProductByIdQuery(id)); return Ok(result); } } |
Publicando Notificações
// Definindo a notificação public record ProductCreatedNotification(Guid ProductId) : INotification; // Handler de notificação public class ProductCreatedEmailHandler : INotificationHandler<ProductCreatedNotification> { public async Task Handle(ProductCreatedNotification notification, CancellationToken cancellationToken) { // Enviar e-mail } } // Outro handler para a mesma notificação public class ProductCreatedLogHandler : INotificationHandler<ProductCreatedNotification> { public async Task Handle(ProductCreatedNotification notification, CancellationToken cancellationToken) { // Registrar log } } // Publicando em um serviço public class ProductService { private readonly IPublisher _publisher; public ProductService(IPublisher publisher) { _publisher = publisher; } public async Task CreateProduct(CreateProductCommand command) { // Cria o produto... await _publisher.Publish(new ProductCreatedNotification(productId)); } } |
Vantagens do TinyMediator:
Mais leve que o MediatR original
Melhor performance (menos alocações)
API similar ao MediatR (facilita migração)
Suporte a requests com e sem resposta
Pub/Sub integrado
Diferenças Principais para o MediatR:
Não tem suporte nativo a pipelines/middlewares
API mais enxuta
Foco em desempenho
3.
Brighter
Pacote: Paramore.Brighter,
mais completo, suporta padrões Command e Event além de CQRS
Instalação
dotnet add package Paramore.Brighter dotnet add package Paramore.Brighter.Extensions.Hosting |
Configuração básica
// No Program.cs builder.Services.AddBrighter(options => { options.HandlerLifetime = ServiceLifetime.Scoped; }) .UseExternalBus(new InMemoryMessageProducer()) // Para comandos assíncronos .UseInMemoryOutbox(); // Opcional para resiliência |
Definindo Commands
public class CreateProductCommand : Command { public string Name { get; } public decimal Price { get; } public CreateProductCommand(string name, decimal price) : base(Guid.NewGuid()) { Name = name; Price = price; } } |
Implementando Handlers
public class CreateProductCommandHandler : RequestHandler<CreateProductCommand> { public override CreateProductCommand Handle(CreateProductCommand command) { // Lógica para criar produto Console.WriteLine($"Criando produto: {command.Name}"); return base.Handle(command); } } |
Registrando Handlers
builder.Services.AddScoped<CreateProductCommandHandler>(); |
Enviando Commands (Sync)
public class ProductController : ControllerBase { private readonly IAmACommandProcessor _commandProcessor; public ProductController(IAmACommandProcessor commandProcessor) { _commandProcessor = commandProcessor; } [HttpPost] public IActionResult Create([FromBody] ProductRequest request) { var command = new CreateProductCommand(request.Name, request.Price); _commandProcessor.Send(command); return Ok(); } } |
Enviando Commands (Async)
public async Task<IActionResult> CreateAsync([FromBody] ProductRequest request) { var command = new CreateProductCommand(request.Name, request.Price); await _commandProcessor.SendAsync(command); return Ok(); } |
Publicando Events
public class ProductCreatedEvent : Event { public string Name { get; } public ProductCreatedEvent(Guid id, string name) : base(id) { Name = name; } } // Handler do Evento public class ProductCreatedEventHandler : RequestHandler<ProductCreatedEvent> { public override ProductCreatedEvent Handle(ProductCreatedEvent @event) { Console.WriteLine($"Produto criado: {@event.Name}"); return base.Handle(@event); } } // Publicando _commandProcessor.Publish(new ProductCreatedEvent(Guid.NewGuid(), "Novo Produto")); |
Quando Usar o Brighter:
Quando você precisa de resiliência avançada (retry, circuit
breaker)
Para sistemas distribuídos com mensageria
Quando precisa de padrão Outbox
para garantia de entrega
O Brighter tem uma curva de aprendizado um pouco maior, mas oferece recursos poderosos para sistemas complexos.
E estamos conversados...
"Como guardaste a palavra da minha paciência, também eu te guardarei da hora da
tentação que há de vir sobre todo o mundo, para tentar os que habitam na terra."
Apocalipse 3:10
Referências:
NET - Unit of Work - Padrão Unidade de ...