Consuming API in microservices using http

Utilizando comunicação síncrona via HTTP para comunicação entre dois microsserviços

Esse contexto será desenvolvido utilizando um microservices chamado “ms-order” e outro chamado “ms-delivery”. O primeiro exporá as API’s e o segundo microservices efetuará as requisições via HTTP para obtenção dos detalhes de um pedido no “ms-order”.

Criando o microservices “ms-order”

Nesse microsserviço utilizaremos um exemplo de negócio do marketplace ‘order’. Certifique de configurar o docker previamente com o MongoDB e RabbitMQ. Abaixo execute o DevPrime CLI. Nesse contexto crie no RabbitMQ as filas “orderevents e deliveryevents”.

dp new ms-order --state mongodb --stream rabbitmq --marketplace order --init

Ao finalizar o primeiro microsserviço estará pronto para produção e com as API’s construidas pelo acelerador do DevPrime CLI. Execute o microservices “ms-order”
.\run.ps1 ou ./run.sh (Linux, macOS)

Abra o microsserviço “ms-order” navegador na url “https://localhost:5001 ou http://localhost:5000” e efetue um post na API para criar um pedido.

Criando o microservices “ms-delivery”

Agora criaremos o ms-delivery e adicinaremos na sequência um aggregate root chamado “Delivery”.
dp new ms-delivery --state mongodb --stream rabbitmq

Entre na pasta ms-delivery e adicione um Aggregate Root
dp add aggregate Delivery

Após executar os comandos abra o arquivo “Delivery.cs” na pasta ms-delivery/src/Core/Domain/Aggregates/Delivery e adicione o código abaixo no aggregate root “Delivery"para complementar a funcionalidade necessária a nossa implementação.
code src/Core/Domain/Aggregates/Delivery/Delivery.cs

namespace Domain.Aggregates.Delivery;
    public class Delivery : AggRoot
    {
       public DateTime Started { get; private set;}
       public DateTime Finished { get; private set;}
       public Guid OrderID { get; private set;}
       public double Total { get; private set;}      
    }

Após adicionar o código execute o acelerador do DevPrime CLI para construir as implementações básicas do microsserviço “ms-delivery”
dp init

Ao final você já terá o microsserviço “ms-delivery” pronto para utilizarmos. Para atender o nosso cenário faremos algumas customizações. No primeiro momento é importante lembrar de alterar as portas no Adapter de Web no arquivo de configuração ms-delivery/src/App/appsettings.json para 5002 e 5003 para evitar conflito com o “ms-order” quando em excursão.
code src/App/appsettings.json

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

Após alterar pode executar o projeto e confirmar a mudança de portal pelo log do DevPrime. É importante reforçar a presença o log automático fornecido pelo adapter de Observability. Finalize o projeto e siga os próximos passos.

Criando um DomainEvent e um DTO

Nós vamos agora criar um evento de dominío ‘DeliveryGetOrder’ e um Handler para implementarmos a chamada de acesso externo utilizando http. O DevPrime CLI oferece um acelerador para adicionar eventos de dominíos.

Nós precisaremos de um Data Transfer Object (DTO) para converter o resultado da API “ms-order”. O primeiro passo será criar a pasta src/Core/Application/EventHandlers/Delivery/Model caso não exista e adicionar um novo arquivo “OrderCreated.cs” com o texto abaixo.

Abra o arquivo pelo vscode
code src/Core/Application/EventHandlers/Delivery/Model/OrderCreated.cs

Adicione o código

namespace Application.EventHandlers.Delivery.Model;
    public class OrderCreated: ServicesResult
    {
        public double Total { get; set; }
    }

Agora que você já tem um Data Transfer Object (DTO) nós vamos adicionar um evento de domínio. E para facilitar o processo utilizaremos um acelerador do DevPrime CLI.
dp add domainevent DeliveryGetOrder -agg Delivery

Após confirmar localize o arquivo “DeliveryGetOrderEventHandler.cs” na pasta /src/Core/Application/EventHandlers/Delivery e implememte a consulta ao serviço externo utilizando o Adapter Services pelo comando “Dp.Services.HTTP.DpGet”.

O nosso objetivo nesse exemplo será avaliar o retorno “result”. Em caso de sucesso com o código “200” nós retornaremos o valor do pedido.

using Application.EventHandlers.Delivery.Model;
namespace Application.EventHandlers.Delivery;


public class DeliveryGetOrderEventHandler : EventHandler<DeliveryGetOrder, IDeliveryState>
{
    public DeliveryGetOrderEventHandler(IDeliveryState state, IDp dp) : base(state, dp)
    {
    }

    public override dynamic Handle(DeliveryGetOrder domainEvent)
    {
        // Aggregate Root
        var delivery = domainEvent.Get<Domain.Aggregates.Delivery.Delivery>();

        // External http request using Services Adapter
        var url = $"https://localhost:5001/v1/order/{delivery.OrderID}";

        // Yype dynamic
        var result = Dp.Services.HTTP.DpGet<OrderCreated>(url);

        //Analysis result
        if (result.Status.Equals(200))
            return result.Total;
        else
            return null;
    }
}

Ness exemplo o Adapter de Services acessará uma API baseada no DevPrime via HTTP e utilizando o comando “DpGet” especifico para API’s baseadas no DevPrime. Ao utilizar o DpGet ele já identifica o resultado e converte no formato “OrderCreated” automatiamente simplificando o processo.

Adicionando um DomainEvent no Aggretate

A última etapa é modificar o método “Add()” presente no Aggretate root “Delivery”. Adicione a implementação para emitir o evento “DeliveryGetOrder()” que será interceptado pelo Handler “DeliveryGetOrderEventHandler” implementado anteriormente.
code src/Core/Domain/Aggregates/Delivery/Delivery.cs

public virtual void Add()
{
          Dp.Pipeline(Execute: () =>
          {
            ValidFields();
              ID = Guid.NewGuid();

              var ordertotal = Dp.ProcessEvent(new DeliveryGetOrder());
              if (ordertotal != null)
              {

                  Total = ordertotal;
                  Dp.ProcessEvent(new DeliveryCreated());
              }
              else
              {
                  throw new PublicException("Can not confirm order");
              }            
          });
}

Se conseguirmos obter o valor do pedido nós vamos atualizar o aggregate e emitir o evento “DeliveryCreated” propagando o fato de negócio. Em caso negativo nós emitiremos um PublicException
que retornará ao chamador dessa API uma informação de falha no processo.

Demonstrando o cenário de Delivery

Para inicia a demonstração do cenário é necessário executar o microservices “ms-order” e “ms-delivery” ao mesmo tempo. Como estão em portas diferentes nós conseguiremos demonstrar o cenário com tranquilidade.

Microservices: Order

  1. Abra o navegador na API do microservices “ms-order” em [http://localhost:5000 ou https://localhost:5001]
  2. Efetue um post na API ‘/v1/order” colocando preenchendo o customerName e total.
{
  "customerName": "Bill",
  "customerTaxID": "string",
  "itens": [
    {
      "description": "string",
      "amount": 0,
      "sku": "string",
      "price": 0
    }
  ],
  "total": 1000
}
  1. Faça um get na API ‘/v1/order’ e localize o código do pedido ‘id’

Microservices: Delivery

  1. Abra o navegador na API do microservices “ms-order” em [http://localhost:5002 ou https://localhost:5003]
  2. Efetue um post na API ‘/v1/delivery’ informando o código do pedido em ‘orderID’.
{
  "started": "2022-03-07T17:54:41.920Z",
  "finished": "2022-03-07T17:54:41.920Z",
  "orderID": "",
  "total": 0
}
  1. O Microservices “ms-delivery” irá consultar a API order utilizando o Adapter Services e retornar o valor total do pedido. O próximo passo é criar um registro no microsserviço de delivery e emitir um evento no Stream
{
  "success": true
}

DevPrime Version

Você também consegue acompanhar do lado do microsserviço Order a solicitação ‘GET’

DevPrime Version

  1. Nesse novo cenário gere uma GUID com orderID inexistente e efetue um post na API ‘/v1/delivery’.
{
  "started": "2022-03-07T17:54:41.920Z",
  "finished": "2022-03-07T17:54:41.920Z",
  "orderID": "a316f224-c8fd-4d4d-8495-600f9fea7aaa",
  "total": 0
}
  1. Ao consultar o microsserviço “Order” sobre um pedido inexistente a API de Delivery retorna um erro.
{
  "success": false,
  "error": {
    "type": "Public",
    "trace": "05d359f0-f99a-4f63-8f89-0ac87ac36edd",
    "exceptions": [
      "Can not confirm order"
    ]
  }
}

Parabéns 🚀🚀🚀.

Última modificação March 10, 2022 (615ec2b)