Comunicação assíncrona entre microsserviços com RabbitMQ

Neste cenário, serão implementados dois microsserviços utilizando comunicação assíncrona com RabbitMQ, utilizando a plataforma DevPrime. Ao receber uma requisição, o microsserviço “ms-order” processará suas regras e, em seguida, emitirá um evento para o microsserviço “ms-payment”.

As aplicações baseadas no DevPrime incorporam nativamente uma abordagem de arquitetura dirigida por eventos (Event-Driven Architecture). Elas recebem eventos externos, processam regras de negócio, emitem eventos internos e, quando necessário, propagam os eventos externamente utilizando o adaptador Stream, que se conecta nativamente com RabbitMQ, Kafka e outras plataformas.

Agora, montaremos uma demonstração envolvendo dois microsserviços. O primeiro, “Order”, receberá pedidos, enquanto o segundo, “Payment”, processará os pagamentos. No exemplo, utilizaremos dois containers criados no Docker. É fundamental subir os containers MongoDB e RabbitMQ.

Se você já possui os serviços MongoDB e RabbitMQ localmente, altere as configurações no arquivo /src/App/app/appsettings.json, nas seções State e Stream, com suas credenciais.

Cheklist e preperação do ambiente inicial:

Código-fonte de exemplo com os microsserviços Order e Payment

Este artigo implementará o código de exemplo abaixo para todos os assinantes da plataforma Devprime. Você não precisa baixá-lo e pode seguir na implementação nos próximos tópicos.

1
git clone https://github.com/devprime/devprime-microservices-order-payment

Importante: Caso tenha baixado o projeto, é necessário executar o comando dp stack na pasta clonada para atualizar a sua licença Devprime nos projetos.

1
dp stack

Criando o primeiro microsserviço “Order”

Utilizaremos o DevPrime CLI para a criação dos microsserviços. Neste exemplo, informaremos o nome da nova aplicação, o tipo de serviço de Stream como “RabbitMQ” para emissão de eventos assíncronos e o State como “MongoDB” para a persistência dos dados. Para iniciar um novo microsserviço, utilize o comando de exemplo apresentado. Uma nova aplicação será criada em segundos e estará pronta para produção.
dp new Order --stream rabbitmq --state mongodb

Adicionando uma regra de negócio de pedidos via Devprime Marketplace
dp marketplace order

Executando acelerador para implementação de código baseado o Domain. Ao executar o comando a primeira vez nessa demonstração digite “A” para que possa avançar e realizar as alterações.
dp init

O acelerador implementa automaticamente as classes de “Domain Events” em Domain, “Event Handlers” em Application, “Domain Services” em Application, “Application Services” em Application, a persistência em State, as APIs em Web e alguns testes unitários em Tests como exemplo.

Devprime CLI

Ao executar a aplicação novamente utilizando o script “.\run.ps1” (Windows) ou “.\run.sh” (Linux/macOS) nós já teremos uma nova visualização da sua API. Abra o browser web na url http://localhost:5000 e localize no Swagger o método post e clique em “Try it out".

Preencha dos dados do JSON para depois clicar em “execute”. Retorne ao prompt de commando acompanhe o resultado no log do microsserviço gerado automaticamente pelo adapter de Observability do Devprime.

Devprime CLI

Para conferir um exemplo de código implementado pelo acelerador acompanhe o código na pasta src/Core/Application/EventHandlers/Order/OrderCreatedEventHandler.cs que notifica via adapter de Stream.

code src/Core/Application/EventHandlers/Order/CreateOrderEventHandler.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public override dynamic Handle(OrderCreated orderCreated)
    {
        var success = false;
        var order = orderCreated.Get<Domain.Aggregates.Order.Order>();
        var destination = Dp.Settings.Default("stream.orderevents");
        var eventName = "OrderCreated";
        var eventData = new OrderCreatedEventDTO()
        {
            ID = order.ID,
            CustomerName = order.CustomerName,
            CustomerTaxID = order.CustomerTaxID,
            Total = order.Total
        };
        Dp.Stream.Send(destination, eventName, eventData);
        success = true;
        return success;
    }

Devprime starting microsserviço

Neste momento, o log demonstra o recebimento de uma requisição HTTP na API “Order”, que a repassa para o serviço de aplicação. Em seguida, a regra de negócio no domínio é processada, emitindo o evento “OrderCreated”. Esse evento tem seu estado persistido no MongoDB e resulta em uma notificação no mundo externo pelo Stream.

Ao acessar o RabbitMQ pelo portal, é possível observar este evento na fila “OrderEvents”. Isso significa que o microsserviço “Order” já cumpriu seu papel e notificou esse fato de negócio para que possa ser percebido por outro serviço.

Como a comunicação neste exemplo é totalmente assíncrona, os eventos na fila do RabbitMQ serão lidos pelo microsserviço “Payment” conforme configurado. Você pode parar a aplicação pressionando “Control” + “C” a qualquer momento e carregá-la novamente quando desejar.

Parabéns !!! Você já tem o microsserviço “Order”

Criando o segundo microsserviço “Payment”

O segundo microsserviço será chamado “Payment”, e iniciaremos sua criação neste momento. Durante a implementação, um item adicional que configuraremos será a ativação do “subscribe” no adaptador de Stream, permitindo que o microsserviço “Payment” receba os eventos emitidos pelo microsserviço “Order”.

Executando o dp new e criando o microsserviços
dp new ms-payment --state mongodb --stream rabbitmq

Adicionando uma regra de negócio de pagamento via Devprime Marketplace
dp marketplace payment

Executando o comando “dp init” da Devprime para implementação de código baseado em Domain-Driven Design (DDD).
dp init

Para executar os dois microsserviços no mesmo ambiente local, é necessário alterar a porta HTTP do microsserviço “Payment”. Na pasta do projeto “ms-payment”, edite o arquivo src/App/appsettings.json, na chave “web”, alterando para 5002 e 5003, conforme o exemplo abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  "Devprime_Web": {
    "url": "https://localhost:5003;http://localhost:5002",
    "enable": "true",
    "enableswagger": "true",
    "PostSuccess": "201",
    "PostFailure": "500",
    "GetSuccess": "200",
    "GetFailure": "500",
    "PatchSuccess": "200",
    "PatchFailure": "500",
    "PutSuccess": "200",
    "PutFailure": "500",
    "DeleteSuccess": "200",
    "DeleteFailure": "500"
  },

Execute a aplicação utilizando o comando abaixo
.\run.ps1 ou ./run.sh (Linux, MacOS)

Se a aplicação rodou está correndo tudo bem. Feche a mesma com “Control+C” e seguimeremos com as implamentações para habilitar o recebimento dos eventos emitidos pelo microsserviços “Order”.

O próximo passo é adicionar a configuração no serviço de Stream e criação de um DTO para recebermos os dados do evento. Você pode fazer todo o processo manualmente ou utilizando o acelerador abaixo.
dp add subscribe OrderCreated -as PaymentService

Ao confirmar a alteração você terá três arquivos modificados para que possamos avançar nas configurações.

1
2
3
src/Core/Application/Services/Payment/Model/OrderCreatedEventDTO.cs
src/Adapters/Stream/EventStream.cs
src/Adapters/Stream/GlobalUsings.cs

Abra o arquivo OrderCreatedEventDTO.cs e adicione as seguintes propriedades:

Visual Studio Code
code src/Core/Application/Services/Payment/Model/OrderCreatedEventDTO.cs

1
2
3
4
5
6
public class OrderCreatedEventDTO                     
  {                                                     
    public Guid OrderID { get; set; }
    public string CustomerName { get; set; }
    public double Value { get; set; }  
  }

Agora, edite o arquivo “EventStream.cs” configurando as propriedades definidas no “OrderCreatedEventDTO”. Modificaremos o serviço de aplicação para utilizar o método “Add”. Essa implementação indica que estaremos realizando um “Subscribe” no evento chamado “OrderCreated”, que será vinculado ao alias “Stream1” no adaptador Stream.
code src/Adapters/Stream/EventStream.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace DevPrime.Stream;
public class EventStream : EventStreamBase, IEventStream
{
    public override void StreamEvents()
    {
        
        Subscribe<IPaymentService, OrderCreatedEventDTO>("Stream1",
        "OrderCreated", (dto, paymentService, Dp) =>
        {
            Dp.Observability.Log("Evento recebido");

            var command = new Payment()
            {
                CustomerName = dto.CustomerName,
                OrderID = dto.OrderID,
                Value = dto.Value
            };
            paymentService.Add(command);           


        });
    }
}

A etapa final é alterar o arquivo de configuração do adaptador de Stream no arquivo src/App/appsettings.json e adicionar o nome da fila / tópico “orderevents” em “subscribe”, conforme exemplo abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
"Devprime_Stream": [
    {
      "Alias": "Stream1",
      "Enable": "true",
      "Default": "true",
      "StreamType": "RabbitMQ",
      "HostName": "Localhost",
      "User": "guest",
      "Password": "guest",
      "Port": "5672",
      "Exchange": "Devprime",
      "ExchangeType": "direct",
      "Retry": "3",
      "Fallback": "State1",
      "Subscribe": [ { "Queues": "orderevents" } ]
    }

Parabéns! Você já tem o microsserviço “Payment” configurado para fazer subscribe na fila/tópico chamado “OrderEvents”.

Comunicação assíncrona entre os microsserviços

Agora você chegou ao grande momento de executar os dois microsserviços. Abra cada um em uma janela e execute para que ambos tenham os serviços ativos. Acesse a URL do microsserviço “Order” em http://localhost:5000 e depois vá em “Post” e clique no botão “Try it out”. Realize um post e acompanhe o incrível resultado, com o detalhamento completo em cada microsserviço e o “Payment” reagindo ao evento recebido.

Neste momento, estamos verificando o resultado do processamento no microsserviço “Order”, que recebe um POST e emite o evento “OrderCreated”. Todos os detalhes são gerados automaticamente pelo adaptador de Observabilidade do Devprime.

[INF][Web]["https://localhost:5001;http://localhost:5000"][Host]["Production"][Parameters]["Appsettings"][RequestId: 3282e6f8-d2bd-4ccf-934a-546276a83038]
[2021-06-19T20:23:07.654-03:00][INF][Web]["HTTP"][Order][Add]["{\n  \"customerName\": \"Ramon\",\n  \"customerTaxID\": \"string\",\n  \"itens\": [\n    {\n      \"description\": \"string\",\n      \"amount\": 0,\n      \"sku\": \"string\",\n      \"price\": 0\n    }\n  ],\n  \"total\": 0\n}"][Origin:"https://localhost:5001/swagger/index.html"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Application][OrderService][Add][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Domain][Order][Add][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Domain][Order][Handle][Event]["OrderCreated"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Application][EventHandler]["OrderCreatedEventHandler"][Event]["OrderCreated"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][State][Type "MongoDB"][Alias "State1"][OrderRepository][Add][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Stream][Type "RabbitMQ"][Alias "Stream1"][Out][Event]["OrderCreated"]["Delivered"]["OrderEvents"]["{\"SagaId\":\"00000000-0000-0000-0000-000000000000\",\"Id\":\"35bc8937-deb8-4e35-a8c4-003171fd6e1b\",\"CorrelationId\":\"06721dfc-fd50-4783-8ea7-df201c22599b\",\"AppId\":\"3282e6f8-d2bd-4ccf-934a-546276a83038\",\"AppName\":\"ms-order\",\"Version\":1,\"Name\":\"OrderCreated\",\"When\":\"2021-06-19T20:23:07.8540357-03:00\",\"Payload\":{\"CustomerName\":\"Ramon\",\"CustomerTaxID\":\"string\",\"Total\":0,\"ID\":\"55a72f16-f4e5-476b-85d9-e86c6e6f390c\"}}"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]

E agora o momento que chega o evento ‘OrderCreated’ no microsserviços “Payment”.

[INF][Web]["https://localhost:5003;http://localhost:5002"][Host]["Production"][Parameters]["Appsettings"][RequestId: 072b12e7-c941-4b4b-9823-cbd6e4425309]
[INF][Stream][Type "RabbitMQ"][Alias "Stream1"][In][Event]["OrderCreated"]["OrderEvents"]["{\"SagaId\":\"00000000-0000-0000-0000-000000000000\",\"Id\":\"35bc8937-deb8-4e35-a8c4-003171fd6e1b\",\"CorrelationId\":\"06721dfc-fd50-4783-8ea7-df201c22599b\",\"AppId\":\"3282e6f8-d2bd-4ccf-934a-546276a83038\",\"AppName\":\"ms-order\",\"Version\":1,\"Name\":\"OrderCreated\",\"When\":\"2021-06-19T20:23:07.8540357-03:00\",\"Payload\":{\"CustomerName\":\"Ramon\",\"CustomerTaxID\":\"string\",\"Total\":0,\"ID\":\"55a72f16-f4e5-476b-85d9-e86c6e6f390c\"}}"][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Application][PaymentService][Add][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Domain][Payment][Add][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Domain][Payment][Handle][Event]["PaymentCreated"][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Application][EventHandler]["PaymentCreatedEventHandler"][Event]["PaymentCreated"][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][State][Type "MongoDB"][Alias "State1"][PaymentRepository][Add][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]

Parabéns! 🚀 Você acabou de desenvolver dois microsserviços utilizando Event-Driven com a plataforma Devprime e o Stream do RabbitMQ.

Para saber mais:

Última modificação September 12, 2024 (cac3c638)