Comunicação assíncrona entre microsserviços com Azure Event Hubs

O Azure Event Hubs é um serviço de streaming de dados nativo da nuvem que pode transmitir milhões de eventos por segundo, com baixa latência, de qualquer origem para qualquer destino e é compatível com o Apache Kafka e com a plataforma Devprime para implementação de microsserviços utilizando a estratégia de Event-Driven Architecture.

As aplicações baseadas no Devprime incorporam nativamente uma abordagem de arquitetura orientada 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 Kafka, 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 Kafka.

Se você já possui os serviços MongoDB e Kafka localmente, altere as configurações no arquivo /src/App/app/appsettings.json, nas seções State e Stream, com as 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. Utilize como referência de implementação e ignore esse passo.

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 e obtendo as configurações no Azure Event Hubs padrão “Kafka”

A criação do serviço no Azure Event Hubs pode ser feita diretamente no portal, CLI ou API. Em seguida, adicionaremos os tópicos “orderevents” e “paymentevents”, e obteremos o hostname e a string de conexão com as credenciais necessárias para acesso ao novo serviço de Event Hubs criado no Microsoft Azure.

Entre no portal do Azure e crie um novo Azure Event Hubs com o nome “my-event-hubs”. Nessa imagem, o nosso serviço será criado com o hostname “my-event-hubs.servicebus.windows.net”. É necessário escolher o tier “Standard ou superior para seguir com esse exemplo”.
Event Hubs

Agora localize a opção “Create Event Hubs” no portal e adicione os tópicos “orderevents” e depois “paymentevents”.
Event Hubs

Confira o exemplo de criação do tópico “orderevents”. Insira os textos em minúsculas e replique no “paymentevents”.
Event Hubs

Após seguir os passos, será possível visualizar os tópicos criados no portal.
Event Hubs

Agora que você já tem os tópicos criados, acesse o menu “Shared access policies” e obtenha apenas o valor do campo “Connection string–primary key” para utilizarmos como a credencial de acesso ao Azure Event Hubs.
Event Hubs

Ao final nós teremos os seguintes os dados para aplicar nas configurações do stream de cada microserviço (Order / Payment).

Item Valor
User $ConnectionString
Password Endpoint=sb://my-event-hubs.servicebus.windows.net/; SharedAccessKeyName=RootManageSharedAccessKey; SharedAccessKey=40DOcvh8xHawS+UxGMvntO17EmqcMd3p2+AEhKuXty8=
HostName my-event-hubs.servicebus.windows.net
Port 9093

É importante lembrar que o valor do “User” e “Port” é fixo conforme apresentado, o “Password” é a string connection–primary key, o “HostName” foi obtido conforme o nome do Azure Event Hub e a Port é fixo 9093.

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 “Kafka” 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 kafka --state mongodb

Adicionando uma regra de negócio de pedidos usando o Devprime Marketplace. Em um cenário normal você implementará usando Domain-Driven Design.
dp marketplace order

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

O acelerador implementará 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 para incoporar a sua estratégia de testes unitários.

Nota: Você pode utilizar qualquer ferramenta Kafka Client para se conectar com os parâmetros acima.

Devprime CLI

Aplicando a credencial de acesso no Stream do microsserviço “Order”

Agora chegou o momento de aplicar as configurações de acesso ao serviço do Azure Event Hubs no Adapter de Stream da Plataforma Devprime por meio do driver do Kafka, habilitando com o modelo de configuração abaixo. Nesse cenário, estamos atuando no ambiente local do desenvolvedor e efetuaremos as alterações no arquivo appsettings.json, conforme exemplo abaixo na pasta do projeto.

Abra pelo Visual Studio Code em “Orders”
code src\App\appsettings.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  "DevPrime_Stream": [
    {      
      "Enable": "true",
      "Alias": "Stream1",
      "Default": "true",
      "StreamType": "Kafka",
      "HostName": "my-event-hubs.servicebus.windows.net",
      "User": "$ConnectionString",
      "Password": "Endpoint=sb://my-event-hubs.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=+dVU38Bs6rt29/3oqXF807s2q2OZUe3e9+AEhGu7g5w=",
      "Port": "9093",
      "Retry": "3",
      "Fallback": "State1",
      "Subscribe": []
    }
  ],

Executando o microsserviço “Order”

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 do Handler OrderCreatedEventHandler.cs que notifica via adapter de Stream um novo pedido criado.

Abra pelo Visual Studio Code em “Order”
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 Kafka 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 Kafka 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 kafka

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:

Abra pelo Visual Studio Code em “Payment”
code src\App\appsettings.json

 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"
  },

Aplicando a credencial de acesso no Stream do microsserviço “Payment”

Agora chegou o momento de aplicar as configurações de acesso ao serviço do Azure Event Hubs no Adapter de Stream da Plataforma Devprime por meio do driver do Kafka, habilitando com o modelo de configuração abaixo. Nesse cenário, estamos atuando no ambiente local do desenvolvedor e efetuaremos as alterações no arquivo appsettings.json, conforme exemplo abaixo na pasta do projeto.

Abra pelo Visual Studio Code em “Payment”
`code src\App\appsettings.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  "DevPrime_Stream": [
    {      
      "Enable": "true",
      "Alias": "Stream1",
      "Default": "true",
      "StreamType": "Kafka",
      "HostName": "my-event-hubs.servicebus.windows.net",
      "User": "$ConnectionString",
      "Password": "Endpoint=sb://my-event-hubs.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=40DOcvh8xHawS+UxGMvntO17EmqcMd3p2+AEhKuXty8=",
      "Port": "9093",
      "Retry": "3",
      "Fallback": "State1",
      "Subscribe": [
        {
          
        }
      ]
    }
  ],

Executando o microsserviço “Payment”

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:

Abra pelo Visual Studio Code em “Payment”
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.

Abra pelo Visual Studio Code em “Payment”
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 appsettings.json e adicionar o nome da fila / tópico “orderevents” em “Subscribe”, conforme exemplo abaixo:

Abra pelo Visual Studio Code em “Payment”
code src\App\appsettings.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  "DevPrime_Stream": [
    {      
      "Enable": "true",
      "Alias": "Stream1",
      "Default": "true",
      "StreamType": "Kafka",
      "HostName": "my-event-hubs.servicebus.windows.net",
      "User": "$ConnectionString",
      "Password": "Endpoint=sb://my-event-hubs.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=40DOcvh8xHawS+UxGMvntO17EmqcMd3p2+AEhKuXty8=",
      "Port": "9093",
      "Retry": "3",
      "Fallback": "State1",
      "Subscribe": [
        {
          "Topics": "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 aba / janela do terminal e execute para que ambos os serviços fiquem 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 e emitindo o evento final em “orderpayments”

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 "Kafka"][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 "Kafka"][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 Kafka.

Para saber mais:

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