Idempotência em microsserviços usando .NET, C# e Devprime
Tags: microsserviços, dotnet, devprime, tutorial
Published: 2023-12-28 17:28:19

O desenvolvimento de microsserviços traz consigo uma nova perspectiva para a arquitetura de software. Enfrentar desafios é algo comum, e isso inclui aspectos familiares de aplicações tradicionais, como a idempotência. Essa terminologia tem raízes na matemática e diz respeito ao conceito de que operações idênticas executadas repetidamente não devem alterar o resultado.

Em uma situação prática, imagine pressionar várias vezes o botão “comprar” no carrinho de compras de um e-commerce, resultando em múltiplos pedidos de compra. Esse problema é bastante recorrente, seja em aplicações tradicionais ou em sistemas distribuídos, como os microsserviços. Além de se preocupar com o processamento redundante na API, é crucial implementar a mesma estratégia para os eventos recebidos.

A plataforma [Devprime](https://devprime.io) revolucionou o desenvolvimento moderno de aplicações ao oferecer um projeto completo de arquitetura de software que utiliza a arquitetura hexagonal e a arquitetura orientada a eventos (event-driven architecture) para microsserviços. Isso permite desenvolver o primeiro microsserviço em apenas 30 minutos e economizar cerca de 70% dos custos de implementação do software no backend. Entre as diversas estratégias oferecidas, um recurso crucial é a abordagem automática de idempotência. Essa estratégia pode ser aplicada tanto a posts recebidos na API quanto a eventos recebidos por meio de streams, como Kafka, RabbitMQ e outros. Para compreender rapidamente o comportamento da idempotência, é possível observar o log a seguir de um microsserviço implementado com a [Devprime](https://devprime.io).

Esse log mostra o momento em que a idempotência é automaticamente iniciada e posteriormente finalizada. Ele representa o primeiro POST na API do microsserviço.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wpafc2ca68tox9urno1.png)

O próximo passo consiste em reproduzir o mesmo POST na API para observar internamente o funcionamento do recurso de idempotência. Isso permitirá identificar um processamento duplicado, como ilustrado na imagem, e o sistema irá rejeitar essa duplicação automaticamente, sem passar pelas regras de negócio que foram executadas no exemplo anterior. É importante ressaltar que essa implementação é realizada de forma automática e é disponibilizada pela plataforma [Devprime](https://devprime.io). Essa abordagem contribui para a redução de erros e dos custos de processamento.

No primeiro artigo desta série, intitulado “[Acelerando o desenvolvimento de microsserviços usando .NET / C# e Devprime](https://dev.to/ramonduraes/acelerando-o-desenvolvimento-de-microsservicos-net-c-e-devprime-5a06)“, demonstramos a criação do primeiro microsserviço. Neste exemplo, utilizaremos o mesmo cenário, efetuando modificações para representar o contexto de idempotência, protegendo automaticamente uma API contra processamento duplicado.

**Instalação**
– NET 7.0 ou superior
 – Visual Studio Code
– Docker
– Devprime CLI.

**Preparando o ambiente**
Para preparar o ambiente inicial utilizando o Docker, faremos uso do banco de dados MongoDB e do RabbitMQ como fila, ambos em um ambiente local no Docker. Adicionalmente, o Redis será empregado como um banco de dados secundário para a funcionalidade de idempotência. É importante assegurar que você tenha o Docker instalado e em funcionamento, com os containers do MongoDB, RabbitMQ e Redis ativos. Para orientações detalhadas sobre como criar a exchange “devprime” e as filas “orderevents” e “paymentevents” no RabbitMQ, e realizar o Bind com a exchange, sugiro consultar a documentação da [Devprime](https://devprime.io). As aplicações utilizarão as credenciais padrão fornecidas na documentação da [Devprime](https://devprime.io).

**Obtendo e configurando a aplicação de exemplo**
Nesse artigo modificaremos um microsserviço de exemplo baseado na plataforma [Devprime](https://devprime.io) para ter funcionalidade de idempotência.

1) Efetue clone no github
git clone [https://github.com/devprime/devprime-microservices-order-payment](https://github.com/devprime/devprime-microservices-order-payment)
2) Entre na pasta clonada e atualize o Stack com o comando abaixo do Devprime CLI
dp Stack
3) Nesse passo vamos configurar as credenciais do MongoDB, RabbitMQ, Redis
a) Entre na pasta ms-order e abra no Visual Studio Code o arquivo appsettings.json
code src/app/appsettings.json

b) Localize e atualize a configuração do Rabbitmq em Stream.

 
```json
 “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”,

      “Threads”: “30”,

      “Buffer”: “5”,

      “Subscribe”: []    

}
```

c) Localize e atualize a configuração do MongoDB (State1) e Redis (State2) no item State.

```json
“DevPrime_State”: [

    {

      “enable”: “true”,

      “alias”: “State1”,

      “type”: “db”,

      “dbtype”: “mongodb”,

      “connection”: “mongodb://mongoadmin:LltF8Nx*yo@localhost:27017”,

      “timeout”: “5”,

      “retry”: “2”,

      “dbname”: “ms-order”,

      “isssl”: “true”,

      “numberofattempts”: “4”,

      “durationofbreak”: “45”

    },

    {

      “enable”: “true”,

      “alias”: “State2”,

      “dbtype”: “redis”,

      “connection”: “127.0.0.1:6379,password=LltF8Nx*yo”,

      “timeout”: “5”,

      “retry”: “2”,

      “durationofbreak”: “45”

    }  

],
```

Agora que você revisou as configurações básicas execute o microsserviço de “Order” e efetue alguns posts na API para observar se ele está funcionando normalmente e gravando no banco de dados os posts repetidos.

Para executar o microsserviço:

```
.\run.ps1 ou ./run.sh (Linux, macOS)
```

Após realizar os primeiros testes citados acima finalize a aplicação para avançar nos próximos passos.

**Habilitando a idempotência na API do microsserviço**
A configuração da funcionalidade de idempotência é realizada no arquivo “appsettings”, na seção “DevPrime_App”, dentro do item “Idempotency”. Para habilitar a idempotência, você deve modificar a opção “Enabled” para “true”. O próximo parâmetro, “Alias”, é responsável por definir o local de armazenamento para persistência, que é definido como “State2”. O parâmetro “Duration” controla o período de duração da idempotência. Já o parâmetro “Flow” determina a estratégia a ser adotada, como por exemplo “backend”. O parâmetro “Scope” estabelece o alcance da atuação, podendo ser “all”, “web” ou “stream”. Por fim, o parâmetro “Action” configura o modo de operação, podendo ser automático ou manual.

Para editar abra o appsettings no Visual Studio Code
code src/app/appsettings.json

```json
    “Idempotency”: {

      “Enable”: “true”,

      “Alias”: “State2”,

      “Duration”: “86400”,

      “Flow”: “backend”,

      “key”: “idempotency-key”,

      “Scope”: “all”,

      “Action”: “auto”

}
```

Agora chegou o momento de realizar o primeiro teste executando o microsserviço

a) Execute o novamente o microsserviço e abra no navegador https://localhost:5001
b) Preencha os dados do post pelo Swagger

```json
{

  “customerName”: “Ramon Durães”,

  “customerTaxID”: “ID887612091”,

  “items”: [

    {

      “description”: “Iphone”,

      “amount”: 1,

      “sku”: “IP15”,

      “price”: 1200

    }

  ],

  “total”: 1200

}
```

Após confirmar nós teremos um post realizado com sucesso conforme demonstrado no log. O momento do “Initialize” o Stack Devprime busca a existência anterior desse objeto de idempotência e não encontrando processa normalmente e depois cria o objeto para identificar um segundo processamento duplicado.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9sjqdtdrjsv5qddpvcf.png)

O mesmo resultado pode ser conferido no Swagger.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmyffxsa6vtqt2pl2t06.png)

Ao efetuar um novo POST repetindo o payload anterior nós já teremos uma resposta diferente no Swagger indicando que não foi possível processar o mesmo conforme o código de erro 500 indicado na API.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgwodhagyvywa7vqwjl2.png)

Uma rápida leitura no log do microsserviço já demonstra que a funcionalidade automática de idempotência oferecida pela plataforma [Devprime](https://devprime.io) descartou esse request duplicado sem que nenhuma ação adicional fosse necessária. É importante ressaltar que pelo log mais curto foi possível identificar que não ocorreu o processamento da regra de negócio.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn4mvvwh0la6dbdbpgla.png)

Ao realizar um novo POST só dessa vez trocando algum valor no payload será possível observar que ele será aceito com sucesso, pois a estratégia atua apenas em eventos duplicados.

**Habilitando uma chave como parâmetro da idempotência no microsserviço**
Agora que você já aprendeu como funciona a idempotência na prática nós vamos customizar o flow para “frondend” para que possamos ter uma chave personalizada que nesse contexto definiremos como  “TransactionID” no campo “Key” conforme demonstrado na configuração abaixo.

    “Idempotency”: {

      “Enable”: “true”,

      “Alias”: “State2”,

      “Duration”: “86400”,

      “Flow”: “Frontend”,

      “key“: “TransactionID“,

      “Scope”: “all”,

      “Action”: “auto”

}

Para editar abra o appsettings no Visual Studio Code
code src/app/appsettings.json

Após editar execute novamente o microsserviço e observe as informações de configuração presentes no log determinado que utilizaremos uma chave personalizada “TransactionID” que deve ser fornecida no Header ao consumir a API.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhzta9vpqgzz4xf0k11e.png)

a) Execute o novamente o microsserviço e abra no navegador https://localhost:5001
b) Desta vez nós faremos um post pelo Postman.
c) Crie um novo post para a url https://localhost:5001/v1/order definindo como raw e JSON e adicione o conteúdo abaixo.

{

  “customerName”: “Ramon”,

  “customerTaxID”: “999871903773”,

  “items”: [

    {

      “description”: “string”,

      “amount”: 0,

      “sku”: “string”,

      “price”: 0

    }

  ],

  “total”: 0

}

Na imagem é possível observar o conteúdo no post no Postman.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ka27q9lmmb8aohhwte7e.png)

Ao realizar um novo POST através do Postman, você receberá imediatamente um erro, indicando que é agora obrigatório incluir uma chave “TransactionID” no Header.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9r9kz58pnfvw8e5r7oa.png)

Agora, retorne ao Postman na seção de Headers e adicione a chave “transactionid” com um valor para conseguir realizar um POST. Isso resultará em um sucesso na operação.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9adjumutenx06cxbqlgv.png)

Ao tentar efetuar novamente um segundo POST idêntico na API, você encontrará um erro, conforme apresentado abaixo no log, indicando que a idempotência rejeitou esse processamento duplicado. Para conseguir realizá-lo, será necessário incluir um novo valor no campo “TransactionID” no Header.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3c4dzo6zwq57cwnmgt6.png)

Neste artigo, demonstramos como utilizar o recurso de idempotência em conjunto com o Stack da plataforma [Devprime](https://devprime.io), que incorpora essa funcionalidade automaticamente, simplificando a experiência do desenvolvedor. No exemplo apresentado, utilizamos a abordagem automática com os parâmetros recebidos na API e, posteriormente, com uma chave personalizada transmitida pelo Header.

Para saber mais e criar uma conta gratuita:
https://devprime.io

[],

Ramon Durães
CEO, Devprime

Image: Freepik