ASP .NET Core -  CQRS e Mediator - III


Hoje vamos continuar a fazer uma breve revisão do CQRS - Command Query Responsibility Segregation e mostrar como a biblioteca MediatR pode nos ajudar quando tratamos com o CQRS.

Continuando o artigo anterior veremos agora o recurso das Notificações ou Notifications da MediatR.

MediatR : Notifications

A biblioteca MediatR oferece suporte para dois tipos de mensagens: resquest/response e notifications.

Agora veremos como usar a MediatR para lidar com eventos de notificação.

Em nosso projeto vamos criar na pasta Services as pastas Events e Notifications.

A seguir vamos criar na pasta Events uma classe LogEvent que estende a interface INotification da biblioteca IMediatR

using MediatR;

namespace DemoCQRS.Services.Events
{
    public class LogEvent : INotification
    {
        public string message;
        public LogEvent(string message)
        {
            this.message = message;
        }

    }
}

Agora precisamos criar handlers ou manipuladores para este evento, e, para isso vamos podemos criar classes que implementam a interface INotificationHandler.

Aqui podemos implementar o tratamento para registrar os eventos em um arquivo, em um banco de dados ou enviar um email, etc. Temos assim várias operações independentes que ocorrem após a um evento.

Na pasta Notifications vamos implementar, a título de exemplo, o tratamento para logar em arquivo, em banco de dados e para enviar um email de notificação.

Vamos começar criando o arquivo FileNotificationHandler que vai permitir gravar as mensagens de notificação em arquivo texto:

using DemoCQRS.Services.Events;
using MediatR;
using System.Threading;
using System.Threading.Tasks;

namespace DemoCQRS.Services.Notifications
{
    public class FileNotificationHandler : INotificationHandler<LogEvent>
    {
        public Task Handle(LogEvent notification,
        CancellationToken cancellationToken)
        {
            string message = notification.message;
            Log(message);
            return Task.FromResult(0);
        }

        private void Log(string message)
        {
            //codigo que escreve a mensagem no arquivo texto
            var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            try
            {
                using (StreamWriter txtWriter = File.AppendText(path + "\\" + "log.txt"))
                {
                    txtWriter.Write("\r\nLog Entry : ");
                    txtWriter.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
                        DateTime.Now.ToLongDateString());
                    txtWriter.WriteLine("  :");
                    txtWriter.WriteLine("  :{0}", message);
                    txtWriter.WriteLine("-------------------------------");
                }
            }
            catch (Exception)
            {
                throw;
            }

        }
    }
}

Aqui a implementação para escrever a mensagem no arquivo texto é bem simples e precisa ser melhorada para uma implementação em produção.

Vamos agora criar o arquivo DatabaseNotificationHandler na pasta Notifications com o código a seguir:

using DemoCQRS.Services.Events;
using MediatR;
using System.Threading;
using System.Threading.Tasks;

namespace DemoCQRS.Services.Notifications
{
    public class DatabaseNotificationHandler : INotificationHandler<LogEvent>
    {
        public Task Handle(LogEvent notification,
        CancellationToken cancellationToken)
        {
            string message = notification.message;
            Log(message);
            return Task.FromResult(0);
        }

        private void Log(string message)
        {
            //codigo para gravar a mensagem no seu banco de dados
        }
    }
}

Aqui eu deixo a implementação a seu cargo pois isso vai depender do banco de dados.

Finalmente vamos criar na mesma pasta o arquivo EmailNotificationHandler que vai herdar de INotificationHandler e vai enviar um email de notificação quando o evento ValueAddedNotification ocorrer :

using DemoCQRS.Data;
using MediatR;
using System.Threading;
using System.Threading.Tasks;

namespace DemoCQRS.Services.Notifications
{
    public class EmailNotificationHandler : INotificationHandler<ValueAddedNotification>
    {
        private readonly FakeData _fakeDataStore;
        public EmailNotificationHandler(FakeData fakeDataStore)
        {
            _fakeDataStore = fakeDataStore;
        }
        public Task Handle(ValueAddedNotification notification, CancellationToken cancellationToken)
        {
            _fakeDataStore.EventOccured(notification.Value, "Email enviado");
            return Task.CompletedTask;
        }
    }
}

Vamos criar ainda na pasta Notifications o arquivo ValueAddedNotification que representa uma notificação :

 public class ValueAddedNotification : INotification
  {
        public string Value { get; set; }
  }

A seguir precisamos incluir em nosso arquivo FakeData o método EventOccured() que vai disparar a notificação :

 public void EventOccured(string value, string evt)
 {
     var indexOfValue = _values.FindIndex(val => val.StartsWith(value));
     _values[indexOfValue] = $"{_values[indexOfValue]}, event: {evt}";
 }

Entendendo o que fizemos :

- Criamos uma classe chamada ValueAddedNotification que implementa INotification, com uma única propriedade chamada Value. Isso é o equivalente a IRequest que vimos antes, mas para notificações;

- A seguir, criamos o gerenciador EmaiNotificationlHandler que implementa  INotificationHandler<ValueAddedNotification>, significando que ele pode manipular esse evento;

- Chamamos o método EventOccured em FakeData, especificando o evento que ocorreu;

Agora precisamos disparar a notificação e fazemos isso no controlador TestesController no método Action Post usando o método Publish do MediatR e acionando a notificação :

       [HttpPost]
        public async Task Post([FromBody] string value)
        {
            await _mediator.Send(new Services.Commands.AddValueCommand.Command
            {
                Value = value
            });
            await _mediator.Publish(new Services.Notifications.ValueAddedNotification { Value = value });
        }

Aqui além de enviar a solicitação AddValueCommand, agora estamos enviando a nossa ValueAddedNotification, desta vez usando o método Publish.

Se quiséssemos, poderíamos ter feito isso diretamente no manipulador para AddValueCommand, mas vamos colocá-lo aqui para simplificar.

Para testar vamos executar o projeto e acionar o endpoint POST /api/Testes e incluir um valor, e,  depois vamos acionar o endpoit GET /api/Testes onde veremos a notificação:

Como esperado, quando adicionamos “teste”, ambos os eventos dispararam e editaram o valor. Apesar de ser um exemplo básico, a principal lição aqui é que podemos disparar um evento e fazer o tratamento muitas vezes, sem que o produtor tenha conhecimento.

Com isso apresentamos de forma resumida os principais recursos da biblioteca MediatR que podemos usar em nossas aplicações ASP .NET Core.

Pegue o projeto aqui :  DemoCQRS.zip (sem as referências)

"Nisto consiste o amor: não em que nós tenhamos amado a Deus, mas em que ele nos amou e enviou o seu Filho como propiciação pelos nossos pecados."
1 João 4:10

Referências:


José Carlos Macoratti