Desafios na Implementação da Arquitetura de Microsserviços

Introdução 

Vimos nos artigos anteriores os benefícios de adotar a arquitetura de microsserviços em comparação com a arquitetura monolítica. Mas a definição da adoção de uma arquitetura deve levar em conta as vantagens e as desvantagens das possíveis alternativas. Sem sombra de dúvidas os benefícios na utilização da arquitetura de microsserviços em relação a arquitetura monolítica são maiores do que as desvantagens. Talvez o termo adequado fosse aumento de complexidade e não desvantagem. Mas independente de qualquer coisa, este aumento de complexidade existe e estar devidamente preparado para saber como endereçar tais itens é fundamental na adoção deste modelo de arquitetura.

Um destes itens, e que vamos tratar neste artigo, é sobre transações distribuídas por diversos microsserviços.

Cenário

Para demonstrar como modelar uma transação distribuída vamos imaginar o seguinte cenário:

Existe um sistema de ecommerce onde o cliente possui um crédito e pode comprar os produtos criando uma ordem de compra e o valor total da ordem de compra é debitado do crédito do cliente.

Vamos focar em descrever apenas a funcionalidade para demonstrar a transação distribuída, um ecommerce real possui outras tantas funcionalidades assim como outros componentes de software.

Para desenvolver esta funcionalidade foi planejado dois microsserviços:

Customer Service: Manipula todas as informações do cliente, incluindo o crédito que o cliente possui.

Order Service: gerencia as informações das ordens dos clientes.

Onde a arquitetura do sistema ficou da seguinte maneira:

Quando o cliente efetua uma compra é gerada uma ordem. E para efetivar esta ordem é preciso garantir que o cliente possui crédito suficiente para tal ação e que debitando o valor deste crédito ele fique igual ou maior que zero. O crédito do cliente não pode ficar negativo após uma ordem ser executada.

Aqui temos um cenário onde para finalizar uma ordem, existe uma interação entre os dois microsserviços: order Service e Customer Service.

Transação em uma arquitetura monolítica

Antes de entrarmos no detalhe de como seria realizada esta transação em um cenário distribuído, vale uma observação de como seria realizada em uma arquitetura monolítica.

Numa arquitetura monolítica esta funcionalidade não seria complexa pois como todos os componentes de software dos quais precisamos estão dentro da mesma aplicação é viável executar todas as ações dentro de uma única transação. Uma opção seria deixar a cargo do meu servidor de aplicação JavaEE (isto num contexto de uma aplicação java) controlar esta transação, a única ação que o desenvolvedor teria que fazer seria anotar seu método com @transactional.

Numa arquitetura distribuída vamos utilizar um design pattern denominado SAGA para nos ajudar a resolver esta questão.

Transação em uma arquitetura de microsserviços – SAGA

O pattern Saga não é uma idéia nova, em 1987 foi lançado um artigo explicando tal mecanismo, então já temos uma ideia de que tais questões há muito tempo já são debatidas e mapeadas.

A idéia básica do SAGA é termos várias transações locais espalhadas em diferentes serviços. Simples e direto. É exatamente isto que temos:

Vamos criar um SAGA para o cenário proposto no início do artigo. 

Então teríamos os seguintes passos para um SAGA concluída com sucesso: 

No passo 1 temos a criação da ordem de compra com o status pendente;

No passo 2 temos a reserva do crédito;

E, finalmente no passo 3 temos a alteração do status da ordem para finalizado;

Analisando este cenário parece muito fácil a criação de um SAGA, mas na realidade é um pouco mais complexo do que estes diagramas demonstram. O primeiro desafio que vamos enfrentar é como realizar um rollback caso ocorra um erro de negócio ao reservar o crédito.

Não tem milagre, devemos implementar manualmente este processo, denominado de transação compensatória, e realizar a chamada explicitamente. Então teremos o seguinte cenário em caso de falha ao reservar o crédito:

Todo integrante da uma transação SAGA que tenha que implementar uma ação para desfazer algo é denominado de transação compensável.

Implementar transações compensáveis nem sempre é fácil pois existem alguns desafios que devem ser endereçados, são eles:

  • Qual será a política para garantir que uma transação compensatória seja executada?
  • Em algumas situações não é possível retornar ao valor original pois outras transações já alteraram o registro.
  • Existem transações que não podem ser desfeitas, ex.: email enviado;

Classificação dos Integrantes de uma transação SAGA.

Acima já citamos um dos tipos de participantes em uma transação SAGA, foi a transação compensável onde deve ser implementada uma transação compensatória para que seja desfeita a ação realizada.

Existem mais dois tipos de integrantes SAGA, são eles: transação de retentativas e transação pivot;

Transação de Retentativas

Como o próprio nome sugere, uma transação de retentativa significa que não existe nenhuma possibilidade de falha negocial e que a transação deve ser executada até ser efetivada.

Um bom exemplo de transação de retentativa é um envio de email. Onde o que pode acontecer é um problema de infra, onde o serviço pode estar off. Por isso a importância de utilizar um mecanismo que tenha a propriedade: at least once, que garante a entrega da mensagem ao menos uma vez. Um message broker como o kafka garante ao menos uma vez a entrega da mensagem.

Transação Pivot

A transação pivot é o passo mais importante na sua transação como um todo, é aquela transação que define se a transação SAGA vai ser efetivada ou se será desfeita, é o próprio go/no go em ação.

No caso do nosso exemplo a transação Pivot é a ação de reservar crédito, repare que se não houver crédito suficiente, a transação como um todo é desfeita.

Toda transação saga deve possuir apenas um integrante do tipo pivot, não mais que um, lembre-se disso.

Outra regra importante é que antes da transação Pivot ficam as transações de compensação e depois da transação Pivot ficam as transações de retentativa.

Implementação SAGA

De uma forma sucinta existem duas maneiras de implementar um SAGA:

  • Coreografia: Quando não há um componente central. Cada serviço produz e escuta os eventos do outro serviço e decide se uma ação deve ser realizada ou não.
  • Orquestração: Quando existe um componente que exerce o papel de coordenação dos passos da transação SAGA. É responsável por centralizar a tomada de decisões e sequenciar a lógica de negócios.

Neste artigo não vamos nos aprofundar nas duas maneiras citadas acima, isto ficará para uma próxima oportunidade, porém é importante conhecer que elas existem e que no caso de uma implementação SAGA uma delas será adotada.

Conclusão

Implementar o pattern SAGA para uma transação composta por diversos microsserviços deve ser uma prática adotada para garantir a integridade de seus dados e aumentar a robustez do seu sistema. Sua empresa está enfrentando este desafio? Quer avaliar o caso conosco? Converse com um de nossos especialistas.