ASP.NET Core -  MediatR Notificações
    Neste artigo vamos apresentar o recurso Notification da biblioteca MediatR usada para realizar a comunicação entre os componentes da aplicação.

O MediatR é uma biblioteca poderosa que implementa o padrão de design Mediator, promovendo desacoplamento e organização na comunicação entre diferentes partes da sua aplicação ASP.NET Core.





Entre seus recursos, o sistema de notificações se destaca por sua simplicidade e flexibilidade para comunicar mensagens de sucesso, erro ou aviso ao usuário.

As notificações do MediatR, também conhecido como Publish, permite transmitir mensagens para vários manipuladores de forma assíncrona. Esse mecanismo informa os componentes do seu aplicativo sobre eventos ou alterações importantes sem lógica complicada.

Os conceitos centrais envolvidos nas notificações do MediatR são:
  • IRequestHandler<TRequest, TResponse>: Interface que define um manipulador de requisições que recebe um tipo de requisição TRequest e retorna um tipo de resposta TResponse.
  • INotificationHandler<TNotification>: Interface que define um manipulador de notificações que recebe um tipo de notificação TNotification.
  • Notification: Classe base para todas as notificações.
  • Mediator: Classe que implementa o padrão Mediator, centralizando o envio de requisições e notificações.

Imagine o cenário onde temos uma aplicação de Vendas online que precisa notificar os clientes por email ou SMS quando seus pedidos foram criados e concluídos. Usando Notificações MediatR podemos realizar esta tarefa com elegância.

Em aplicativos modernos, a execução de tarefas como envio de e-mails ou notificações por SMS de forma assíncrona garante uma experiência de usuário tranquila e evita atrasos desnecessários. Por exemplo, ao criar um pedido, o foco principal deve ser concluir o processo do pedido sem depender de respostas imediatas de serviços de e-mail ou SMS.

O aproveitamento das Notificações MediatR permite o gerenciamento eficiente dessas tarefas assíncronas, permitindo a progressão independente do processo de criação de pedidos. Essa abordagem garante a capacidade de resposta do aplicativo e o desempenho ideal ao enviar notificações por e-mail e SMS em segundo plano, sem afetar a funcionalidade principal do manipulador de criação de pedidos.

Você deseja que os manipuladores de eventos/notificações sejam executados de forma independente e isolada. É aqui que entram as mensagens fora do processo. No entanto, uma única solicitação pode ser tratada por vários manipuladores. Alguma operação independente precisa ocorrer após alguns eventos.

Criando o projeto

Vamos criar um projeto no VS 2022 usando o template ASP.NET Core Web API chamado ApiPedidosNotification para ilustrar como enviar notificações usando o MediatR.

Após criar o projeto vamos incluir o pacote Nuget MediatR no projeto e na classe Program vamos registrar o serviço do MediatR :

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// registra o MediatR 
builder.Services.AddMediatR(cfg =>
        cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
var app = builder.Build();
...

Neste projeto vamos criar a pasta Entities contendo a classe Pedido :

public class Pedido
{
    public int Id { get; set; }
    public string? ClienteNome { get; set; }
    public decimal Total { get; set; }
    public DateTime CriadoEm { get; set; }
}

A seguir vamos criar uma pasta Features no projeto e nesta pasta vamos criar as pastas Commands e Notifications.

Na pasta Commands vamos criar o comando e seu handler para criar um pedido:

1- CriarPedidoCommand

public class CriarPedidoCommand : IRequest<Pedido>
{
    public string? ClienteNome { get; set; }
    public decimal ValorTotal { get; set; }
    public string? ClienteEmail { get; set; }

2- CriarPedidoCommandHandler

public class CriarPedidoCommandHandler : 
                    IRequestHandler<CriarPedidoCommand, Pedido>
{
   private readonly IMediator _mediator;
    public CriarPedidoCommandHandler(IMediator mediator)
    {
        _mediator = mediator;
    }
    public async Task<Pedido> Handle(CriarPedidoCommand request,
                            CancellationToken cancellationToken)
    {
        // Valida o request
        // Cria o pedido na base (a implementar...)
        var pedido = new Pedido
        {
            Id = 1,
            ClienteNome = request.ClienteNome,
            Total = request.ValorTotal
        };
        // Publica a notificação
        await _mediator.Publish(new CriarPedidoNotification() 
                      { Pedido = pedido }, cancellationToken);
        return pedido;
    }
}

O método Publish() do MediatR publica a notificação.

A seguir na pasta Notifications vamos criar a classe de notificação que implementa a interface INotification do MediatR:

public class CriarPedidoNotification : INotification
{
   public Pedido? Pedido { get; set; }

A seguir vamos criar a classe CriarPedidoNotificationHandler que é o manipulador para esta notificação :
 
public class CriarPedidoNotificationHandler : 
               INotificationHandler<CriarPedidoNotification>
{
    private readonly ILogger<CriarPedidoNotificationHandler> _logger;
    public CriarPedidoNotificationHandler(
               ILogger<CriarPedidoNotificationHandler> logger)
    {
        _logger = logger;
    }
    public Task Handle(CriarPedidoNotification notification, 
                          CancellationToken cancellationToken)
    {
        // envia email de confirmação
        _logger.LogInformation($"Email de confirmação enviado para o pedido : 
                                   {notification.Pedido.Id}");
        return Task.CompletedTask;
    }
}

A seguir podemos criar o controlador PedidosController na pasta Controllers :

[Route("api/[controller]")]
[ApiController]
public class PedidosController : ControllerBase
{
    private readonly IMediator _mediator;
    public PedidosController(IMediator mediator)
    {
        _mediator = mediator;
    }
    [HttpPost]
    public async Task<Pedido> Create([FromBody] CriarPedidoCommand command)
    {
        var order = await _mediator.Send(command);
        return order;
    }
}

Executando o projeto teremos o endpoint /api/Pedidos que quanto acionado conforme mostra a figura abaixo:

Ao clicar em Execute teremos o resultado exibido na figura abaixo:

Observando a janela Output vemos o log exibindo a mensagem enviada:

Com isso mostramos de forma objetiva e direta como é simples enviar notificações usando a biblioteca MediatR.

Lembrando que podemos criar quantos manipuladores (Handlers) quisermos para uma única notificação (Notification). Cada manipulador irá lidar com a notificação de uma maneira diferente, permitindo que você tenha várias ações acontecendo em resposta a uma única notificação.

Por exemplo, você pode ter uma única notificação "PedidoCriadoNotification" e criar vários manipuladores para lidar com ela de maneiras diferentes.

Por exemplo:

public class PedidoCriadoNotification : INotification
{
    public Pedido Pedido { get; set; }
}
public class EnviarEmailPedidoCriadoHandler : 
             INotificationHandler<PedidoCriadoNotification>
{
    public Task Handle(PedidoCriadoNotification notification,
                            CancellationToken cancellationToken)
    {
        // Lógica para enviar e-mail de confirmação para o pedido criado
        return Task.CompletedTask;
    }
}

public class AtualizarStatusPedidoHandler : 
                INotificationHandler<PedidoCriadoNotification>
{
    public Task Handle(PedidoCriadoNotification notification, 
                  CancellationToken cancellationToken)
    {
        // Lógica para atualizar o status do pedido no sistema
        return Task.CompletedTask;
    }
}
public class NotificarAdminsPedidoCriadoHandler : 
              INotificationHandler<PedidoCriadoNotification>
{
    public Task Handle(PedidoCriadoNotification notification, 
                    CancellationToken cancellationToken)
    {
        // Lógica para notificar administradores sobre o novo pedido criado
        return Task.CompletedTask;
    }
}

Cada manipulador irá reagir à notificação PedidoCriadoNotification de forma diferente. Por exemplo, o EnviarEmailPedidoCriadoHandler pode enviar um e-mail de confirmação para o cliente, o AtualizarStatusPedidoHandler pode atualizar o status do pedido no sistema e o NotificarAdminsPedidoCriadoHandler pode notificar administradores sobre o novo pedido criado.

Quando você publica uma única notificação PedidoCriadoNotification, todos esses manipuladores serão acionados em sequência para lidar com a notificação, executando suas respectivas lógicas. Isso é uma das vantagens do uso do MediatR e do padrão Mediator - permite que você desacople as diferentes partes do seu sistema e reaja de forma flexível a eventos específicos.

Agora em vez de repetir essa lógica no manipulador, podemos usar o recurso  Behavior, e,  na próxima parte do artigo veremos como implementar este recurso.

Pegue o projeto aqui:  ApiPedidosNotification.zip

E estamos conversados ...

"Porque os que são segundo a carne inclinam-se para as coisas da carne; mas os que são segundo o Espírito para as coisas do Espírito.Porque a inclinação da carne é morte; mas a inclinação do Espírito é vida e paz."
Romanos 8:5,6

Referências:


José Carlos Macoratti