ASP .NET Core  5 - Implementando CQRS com Mediator - IV


Neste artigo vamos continuar a implementação do CQRS - Command Query Responsibility Segregation usando o padrão Mediator.(Mediatr)

Continuando o artigo anterior vamos agora usar o recurso Notifications do MediatrR.

MediatR - Usando o recurso das notificações

Um dos recursos que a biblioteca MediatR oferece é a opção de gerar uma notificação em seu código e, em seguida, ter várias ações realizadas por trás dele.

Assim,  em vez de colocar toda a sua lógica de negócios na ação do controlador (ou mesmo delegar a vários serviços), você pode publicar uma notificação. As notificações podem ser publicadas de forma síncrona ou assíncrona.

Na sua essência os Requests Command não retornam nenhuma informação, assim para ser informado que a requisição foi concluída com sucesso, ou não, podemos implementar notificações.

Este recurso funciona da seguinte forma:

- Após uma requisição ser atendida pelo método Handler de uma classe CommandHandler podemos invocar o método Publish() e passar via parâmetro o objeto Notification emitindo uma notificação para todo o sistema;

- A biblioteca MediatR vai procurar por uma classe que implementou a interface INotificationHandler<tipo da notificacao> e invocar o método Handler() para processar aquela notificação,

- Todos os tratadores de eventos ou Events Handlers que estiverem ouvindo notificações do tipo do objeto publicado será notificados e poderão processar o objeto Notification;

Assim, para implementar as notificações inicialmente é necessário definir os objetos notificação que são classes que vão conter apenas os dados necessários para processar a informação.

Para o nosso artigo vamos criar a classe CustomerActionNotification na pasta Services/Notifications do projeto API que vai implementar a interface INotification:

using MediatR;
namespace DemoMediator.Application.Services.Notifications
{
    public class CustomerActionNotification : INotification
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public ActionNotification Action { get; set; }
    }
}

Aqui estamos usando a enumeração ActionNotification que define os seguintes valores:

namespace DemoMediator.Application.Services.Notifications
{
    public enum ActionNotification
    {
        Created = 1,
        Updated = 2,
        Deleted = 3
    }
}

Esta classe vai enviar notificações para as operações de inclusão, alteração e exclusão de dados.

Vamos criar outra classe de notificação chamada ErrorNotification :

using MediatR;
namespace DemoMediator.Application.Services.Notifications
{
    public class ErrorNotification : INotification
    {
        public string Error { get; set; }
        public string Stack { get; set; }
    }
}

Esta classe de notificação vai ser usada para notificar erros ocorridos durante os requests.

Precisamos agora definir a classe Notification Handler que vai escutar as notificações e aqui podemos fazer diversas implementações. Neste artigo como exemplo vamos exibir as notificações no console:

Assim na pasta Events vamos criar a classe LogEventHandler e registrar as notificações criadas implementando o método Handle para tratar cada tipo de notificação:

using DemoMediator.Application.Services.Notifications;
using MediatR;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace DemoMediator.Application.Services.Events
{
    public class LogEventHandler :
                 INotificationHandler<CustomerActionNotification>,
                 INotificationHandler<ErrorNotification>

    {
        public Task Handle(CustomerActionNotification notification, CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                Console.WriteLine($"Customer {notification.Name} - {notification.Email} was
{notification.Action.ToString().ToLower()} successfuly");
            });
        }

        public Task Handle(ErrorNotification notification, CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                Console.WriteLine($"ERROR : '{notification.Error} \n {notification.Stack}'");
            });
        }
    }
}

Agora podemos alterar o código dos Comandos e Consultas definidas na pasta Commands e Queries.

Para mostrar como usar as notificações vou exibir a seguir a implementação feita para a classe UpdateCustomerCommand  destacando as notificações usadas:

using DemoMediator.Application.Services.Notifications;
using DemoMediator.Domain.Interfaces;
using MediatR;
using System.Threading;
using System.Threading.Tasks;

namespace DemoMediator.Application.Services.Commands
{
    public class UpdateCustomerCommand : IRequest<int>
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public class UpdateProductCommandHandler : IRequestHandler<UpdateCustomerCommand, int>
        {
            private readonly ICustomerRepository _context;
            private readonly IMediator _mediator;

            public UpdateProductCommandHandler(ICustomerRepository context, IMediator mediator)
            {
                _context = context;
                _mediator = mediator;
            }
            public async Task<int> Handle(UpdateCustomerCommand command, CancellationToken cancellationToken)
            {
                var customer = await _context.GetById(command.Id);

                if (customer == null)
                {
                    await _mediator.Publish(new ErrorNotification
                    {
                        Error = "Customer not found",
                        Stack = "customer is null"
                    }, cancellationToken);

                    return default;
                }
                else
                {
                    customer.Name = command.Name;
                    customer.Email = command.Email;

                    _context.Update(customer);

                    await _mediator.Publish(new CustomerActionNotification
                    {
                        Name = command.Name,
                        Email = command.Email,
                        Action = ActionNotification.Updated
                    }, cancellationToken);

                    return customer.Id;
                }
            }
        }
    }
}

E assim concluímos esta série de artigos sobre a biblioteca MediatR.

"Disse-lhe Jesus: Porque me viste, Tomé, creste; bem-aventurados os que não viram e creram."
João 20:29

Referências:


José Carlos Macoratti