![](https://ramonduraes.net/wp-content/uploads/2023/08/image-artigo-1000x600.jpg)
Idempotência em microsserviços usando .NET, C# e Devprime
22 de agosto de 2023O 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 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.
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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem1-1024x424.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. 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“, 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. As aplicações utilizarão as credenciais padrão fornecidas na documentação da Devprime.
Obtendo e configurando a aplicação de exemplo
Nesse artigo modificaremos um microsserviço de exemplo baseado na plataforma Devprime para ter funcionalidade de idempotência.
1) Efetue clone no github
git clone 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.
“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.
“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
“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
{
“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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem3-1024x372.png)
O mesmo resultado pode ser conferido no Swagger.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem4.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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem5.png)
Uma rápida leitura no log do microsserviço já demonstra que a funcionalidade automática de idempotência oferecida pela plataforma Devprime 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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem6-1024x173.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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem7-1024x381.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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem8-1024x433.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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem9-1024x83.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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem10-1024x246.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.
![](https://ramonduraes.net/wp-content/uploads/2023/08/imagem11-1024x183.png)
Neste artigo, demonstramos como utilizar o recurso de idempotência em conjunto com o Stack da plataforma Devprime, 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