Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição do episódio do nosso canal, Dev Eficiente. O episódio completo pode ser visto no canal.
Introdução
No mundo do desenvolvimento de software, especialmente quando falamos de APIs, um conceito é fundamental para garantir a robustez e a confiabilidade das aplicações: resiliência. Neste post, vamos explorar este conceito através da ótica da idempotência e entender como ela pode ser aplicada no desenvolvimento de APIs.
O que é Idempotência?
Uma definição que considero interessante sobre idempotência é a seguinte: Um método é considerado "idempotente" se o efeito gerado a partir de várias invocações idênticas para este método for o mesmo que o efeito de uma única invocação.
Isso é especialmente importante em APIs que precisam lidar com retries (tentativas repetidas de uma mesma operação), como no caso de gateways de pagamento. Imagine que você está processando um pagamento e, por algum motivo, a resposta da API demora a chegar. Se o cliente fizer uma nova tentativa, a API precisa garantir que o pagamento não será processado duas vezes. É aí que entra a idempotência.
No contexto de APIs HTTP, verbos como PUT e DELETE são naturalmente idempotentes. Se a implementação seguir a especificação, em uma tentativa de deletar o mesmo recurso duas vezes, o resultado será o mesmo: o recurso será deletado (ou já estará deletado). No entanto, o verbo POST, que geralmente é usado para criar novos recursos, não é idempotente por padrão. Se você fizer dois POSTs com os mesmos dados, pode acabar criando dois recursos idênticos, o que não é desejável em muitos casos, como no processamento de pagamentos.
Implementando Idempotência
Na minha implementação, utilizei o Spring Framework para criar uma API que suporta idempotência. A ideia é simples: o cliente gera uma chave de idempotência(idempotency-key) e a envia junto com a requisição. A API, por sua vez, verifica se já existe uma resposta associada a essa chave. Se existir, ela retorna a mesma resposta, sem reprocessar a operação.
Essa abordagem é muito útil em cenários onde o cliente pode fazer várias tentativas de uma mesma operação, como em políticas de retry. Por exemplo, se a primeira tentativa de pagamento falhar por um erro temporário, o cliente pode tentar novamente com a mesma chave de idempotência, e a API garantirá que o pagamento não será processado mais de uma vez.
Idempotência como via para suporte de Retries
No caso de um timeout ou erro temporário, a aplicação cliente pode tentar novamente a mesma operação. No entanto, é importante que essas tentativas sejam feitas de forma controlada, para evitar sobrecarregar o servidor ou gerar resultados indesejados. A idempotência, como vimos, é uma peça fundamental nesse processo, pois garante que as tentativas repetidas não causem efeitos colaterais.
Exemplo Prático
Na minha implementação, utilizei alguns recursos do Spring para facilitar o processo de idempotência. Um deles é o ResponseBodyAdvice, que permite interceptar a resposta da API antes que ela seja enviada ao cliente. Com isso, consigo verificar se a chave de idempotência foi passada e, se necessário, salvar a resposta para futuras requisições.
Além disso, utilizei um aspecto para verificar se a chave de idempotência já foi utilizada. Se a chave já estiver associada a uma resposta, a API retorna essa resposta imediatamente, sem reprocessar a operação.
E não posso simplesmente verificar se os parâmetros são exatamente os mesmos?
A abordagem de match de parâmetros é uma possibilidade. O maior perigo dela é que esta é uma interpretação feita pela API sem nenhum indicativo nítido do lado do cliente.
Precisa existir um combinado muito bem definido para assumir que duas requisições com exatamente os parâmetros significam a mesma coisa. Caso contrário a API pode ignorar o processamento quando deveria executar o fluxo normal.
Conclusão
A resiliência é um conceito muito importante para o desenvolvimento de APIs robustas e confiáveis. Ao implementar uma política de idempotência, você garante que operações críticas, como o processamento de pagamentos, não sejam executadas mais de uma vez, mesmo em cenários de falha. E ao utilizar retries de forma controlada, você aumenta a resiliência da aplicação cliente, garantindo que ela possa se recuperar de falhas temporárias minimizando o comprometimento da operação como um todo.
Se você já implementou algo semelhante ou tem sugestões de como melhorar essa abordagem, deixe seu comentário! Será um prazer discutir mais sobre o assunto.
Se você quiser se aprofundar mais nesse tema, recomendo explorar o conteúdo da Jornada Dev + Eficiente, onde há um módulo completo sobre resiliência, com exercícios práticos para você aplicar no seu dia a dia.
Deixe seu comentário abaixo, seja ele positivo ou construtivo, e terei o maior prazer em responder. Até a próxima!
Top comments (0)