MediatR no .NET 6.0

MediatR é inspirado no Mediator Pattern e é utilizado para reduzir a dependência de comunicação entre vários objetos. Esse padrão fornece uma classe como um mediador que normalmente lida com todas as comunicações entre diferentes classes.

Essa biblioteca foi criada por Jimmy Bogard, o mesmo autor do AutoMapper, e é muito utilizado ao implementar o padrão CQRS (Command and Queries Responsability Segregation) (assunto extenso que abordaremos em outro artigo).

Prós em utilizar o MediatR:

  • Componentes desacoplados – Uma classe depende apenas do mediador, que remove a dependência entre diferentes componentes para que a comunicação entre as classes seja encapsulada no mediador.
  • Separação de interesses – À medida que o mediador contém um nível abstrato de implementação para comandos e consultas, cada manipulador (ou caso de uso) tem seu próprio nível de contexto de execução.
  • Facilidade para testar – Criar testes unitários e integrados é bem simples e prático com o MediatR.

Contras:

  • Complexidade – Devido ao peso da implementação do mediador, isso pode se tornar complexo para qualquer time/desenvolvedor sem um entendimento adequado.

Neste artigo, aprenderemos como configurar e utilizar o MediatR para envio de commands, queries e notifications no .NET 6.0.

Pré-requisitos

API

O primeiro passo é configurar a classe program.cs:

A extensão AddMediatRApi() é responsável por registrar a injeção de dependência do MediatR na solução, sendo necessário passar algum tipo de objeto que esteja no projeto onde há implementações de comandos, consultas e notificações:

Com o MediatR é possível realizar três tipos de operações:

  • Queries: São responsáveis pelas consultas na base de dados e retornam objetos do tipo DTOs ou ViewModels.
  • Commands: São responsáveis pelas ações que realizam alguma alteração na base de dados e pode ou não retornar algum dado.
  • Notifications: Eventos disparados de forma assíncrona para realizar algum tipo de ação, geralmente para notificar quando um command foi executado com ou sem sucesso.

No exemplo abaixo, temos uma query que recebe um objeto de entrada (ProductByIdQuery) que é implementado com a interface IRequest, onde é informado o tipo de objeto de saída (ProductByIdQueryResult).

O Handler (ProductByIdQueryHandler) é implementado utilizando a interface IRequestHandler onde é informado o objeto de entrada e saída IRequestHandler<ProductByIdQuery, ProductByIdQueryResult>:

Para consumir a query, basta injetar a interface IMediator no controller e enviar o objeto de entrada como parâmetro no método Send():

Já o command segue a linha de ser uma ação de manutenção na base, que na sua essência não retorna alguma informações. Nesse caso utilizamos o notification para gerar eventos, como por exemplo, ao final das ações de gravação no banco de dados é necessário notificar que um produto foi cadastrado, e também é necessário enviar um e-mail para o cliente responsável, conforme o código abaixo:

Ao chamar o método Publish() passando o objeto de entrada do evento, um handler com a implementação de um ou vários INotificationHandler, recebe e processa a mensagem:

Para consumir o command, basta injetar a interface IMediator no controller e enviar o objeto de entrada como parâmetro no método Send();

Em por fim, temos a seguinte estrutura de projeto:

Testando

Para realizar os testes, execute o endpoint GET /api/queries/product no Swagger para verificar o comportamento no envio de query e o endpoint POST /api/commands/product para analisar o envio de command lançando notifications.

Via console, é possível analisar os passos ao realizar um command pelo MediatR:

Finalizando

Em resumo, a utilização do MediatR é uma boa abordagem para separação de leitura e gravação de dados, porém é preciso ter muita cautela para não se tornar um vilão de complexidade no seu projeto. O próprio Martin Fowler faz esse alerta em relação à utilização do CQRS:

Os detalhes completos deste exemplo você encontra no meu GitHub: https://github.com/hgmauri/sample-cqrs-mediatr

Deixe uma resposta