Nos últimos anos, o MediatR tornou-se amplamente utilizado na comunidade .NET. Muitos projetos são construídos a partir de templates que o incorporam, resultando em uma adoção generalizada. No entanto, poucas pessoas questionam a necessidade real de sua utilização.
Em algumas ocasiões, fui questionado sobre os motivos para não usar MediatR. Para muitos desenvolvedores, sua presença em um projeto é considerada um padrão inevitável, algo que não precisa de justificativa ou debate.
Neste artigo, quero explorar essa ferramenta sob uma ótica mais crítica e avaliar seu impacto real na arquitetura de software.
Por que usamos o MediatR?
Quando participo da definição de uma arquitetura, sempre faço a pergunta: Qual problema estamos tentando resolver com MediatR?
Se as justificativas se resumirem a respostas como:
- “Sempre fizemos assim” / “É assim que se faz”
- “Foi assim que aprendi“ /
- “É assim que fulano faz“ / “É assim que fazíamos na empresa XXX” (em geral, essa resposta usada como argumento de autoridade)
- “É um padrão amplamente adotado.”
- “Os templates mais comuns já incluem MediatR.”
Então, é importante ficar atento, pois há fortes indícios de que o uso da biblioteca pode estar sendo motivado mais por convenção do que por necessidade técnica.
Isso não significa que o MediatR seja uma escolha ruim por si só. Meu objetivo não é criticá-lo, mas sim incentivar uma reflexão sobre seus reais benefícios e desafios. Um dos pontos que mais me preocupa é a falsa sensação de desacoplamento que sua adoção pode criar.
MediatR e o Desacoplamento
Uma das principais razões para utilizar o MediatR é a ideia de reduzir o acoplamento entre diferentes partes do código. No entanto, na maioria dos casos, ele é usado para implementar um modelo request/response, criando apenas uma camada de indireção entre quem faz a chamada e quem a atende.
O problema é que essa dependência não desaparece, apenas fica oculta. O chamador ainda depende diretamente do manipulador da requisição, mas agora através de um intermediário, o que pode adicionar complexidade sem um benefício claro.
Essa abordagem pode dificultar a leitura e manutenção do código. Com o MediatR, um simples fluxo de execução pode se tornar mais trabalhoso de entender, exigindo que os desenvolvedores naveguem entre múltiplas classes para compreender a relação entre os componentes.
Isso pode ser insignificante em aplicações pequenas, mas à medida que a complexidade cresce, essa abstração pode se tornar um obstáculo ao invés de um benefício.
Quando o MediatR é realmente útil?
O MediatR brilha quando utilizado em propagação de notificações ou publicação de eventos. Em um cenário 1 para N, onde um componente emite uma mensagem e múltiplos receptores podem agir sobre ela sem depender de respostas diretas, ele pode ser bastante útil.
Além disso, o MediatR pode ser uma solução eficiente para evitar dependências cíclicas dentro do domínio da aplicação.
Por outro lado, se o fluxo continua sendo estritamente sequencial e cada chamada depende da resposta da outra, o benefício do MediatR se torna questionável. Nesses casos, um serviço injetado diretamente pode ser uma solução mais simples, eficiente e direta.
Impacto na Performance
Outro aspecto que raramente é considerado é o impacto na performance. Embora em aplicações pequenas isso não seja um grande problema, em sistemas maiores a indireção extra pode gerar latências desnecessárias.
Cada requisição passa por um pipeline, o que pode aumentar a sobrecarga, especialmente se houver behaviors e middlewares adicionais configurados. Isso pode resultar em maior consumo de memória e pior desempenho em comparação com uma abordagem baseada em injeção de dependência direta.
Portanto, antes de adotar o MediatR, é importante avaliar se essa complexidade adicional realmente compensa em termos de manutenção e performance.
Beleza e Simplicidade
O uso do MediatR adiciona uma camada de indireção, separando a solicitação (request) de sua execução. Essa abordagem pode parecer mais verbosa à primeira vista, mas introduz um belo padrão arquitetural.
Comparação: Implementação Direta vs MediatR em Request/Response
Ao comparar uma implementação direta com uma implementação usando MediatR no padrão request/response, podemos perceber nuances que vão além da simples organização do código. Vamos ilustrar isso com um exemplo prático.
Implementação Direta:
public class PedidoService { private readonly IPagamentoService _pagamentoService; public PedidoService(IPagamentoService pagamentoService) { _pagamentoService = pagamentoService; } public bool ProcessarPedido(Pedido pedido) { return _pagamentoService.RealizarPagamento(pedido); } }
Neste caso, temos um código simples e direto. A dependência é explicitamente declarada e facilmente rastreável, tornando o código mais legível e de fácil depuração.
Por que é belo?
A beleza da implementação direta está na sua simplicidade, clareza e ausência de dúvidas. O fluxo de execução é evidente, o que facilita o entendimento e a manutenção do código. Quando um novo desenvolvedor precisa compreender a lógica, ele não precisa navegar entre múltiplas camadas ou abstrações, tornando o processo mais eficiente.
Implementação com MediatR:
public class ProcessarPedidoCommand : IRequest<bool> { public Pedido Pedido { get; } public ProcessarPedidoCommand(Pedido pedido) => Pedido = pedido; } public class ProcessarPedidoHandler : IRequestHandler<ProcessarPedidoCommand, bool> { private readonly IPagamentoService _pagamentoService; public ProcessarPedidoHandler(IPagamentoService pagamentoService) { _pagamentoService = pagamentoService; } public Task<bool> Handle(ProcessarPedidoCommand request, CancellationToken cancellationToken) { return Task.FromResult(_pagamentoService.RealizarPagamento(request.Pedido)); } }
A beleza do MediatR reside na organização e padronização da comunicação entre componentes da aplicação.
É importante ressaltar que, caso seja necessário adicionar novos comportamentos, como logs, validações ou métricas, podemos fazê-lo por meio de behaviors, conseguimos fazer isso sem modificar diretamente os handlers.
O uso do MediatR adiciona uma camada de indireção. Essa abordagem pode parecer mais verbosa à primeira vista. Nesse contexto, ele reduz o acoplamento direto entre serviços, transformando acoplamento direto em indireto, quase oculto. E aqui mora minha ressalva.
MediatR continua sendo uma boa escolha, mas é preciso ter clareza sobre seus objetivos de uso no projeto.
Conclusão
O MediatR não deve ser adotado automaticamente, apenas porque é um padrão popular. Ele tem muitos méritos, mas deve ser aplicado de forma criteriosa, como praticamente tudo em nossos projetos. Antes de implementá-lo, vale a pena considerar:
- Qual problema ele realmente resolve no contexto do projeto?
- Ele está promovendo um desacoplamento genuíno ou apenas escondendo dependências?
- O código ficaria mais claro e simples sem essa camada adicional?
- O impacto na performance justifica a adoção dessa abordagem?
Ferramentas e padrões de design existem para resolver problemas, não para serem utilizados indiscriminadamente. O MediatR pode ser uma solução interessante, mas sua adoção precisa ser pensada para ser estratégica. Senão, se torna apenas mais um peso que aumenta a complexidade sem trazer benefícios.
E você? Como tem utilizado o MediatR em seus projetos? Tem encontrado mais benefícios ou desafios?
0 comentários