Gosto bastante da abordagem de Caitie McCaffrey para explicar sagas. Neste post, me inspiro na linha de raciocínio dela para explicar este conceito – fundamental para microsserviços.

Aliás, se está interessado em microsserviços, recomendo fortemente que dedique algum tempo lendo outros posts que já escrevi sobre o tema.

Um começo simples

 

Em aplicações monolíticas temos um frontend conecta a um único serviço que, por sua vez, se conecta a apenas um banco de dados que serve como a “grande fonte da verdade”.

Por mais complexas que sejam as operações, podemos usar, quase sempre, por exemplo, o recurso de transações do banco de dados para garantir a preservação da integridade.

Quando a fragmentação em serviços não é uma opção

Imaginemos, entretanto, que não seja possível estabelecer uma “fonte única de verdade”.  Vamos considerar, por exemplo, o desenvolvimento de um serviço para apoiar a compra de produtos associados a viagens. Nesse contexto, desejaríamos ajudar nossos clientes na aquisição de bilhetes aéreos, no aluguel de carros e na reservas em hotéis.

Nossa arquitetura, provavelmente, ficaria assim:

Perceba que a fragmentação em serviços, aqui, não é opção. Temos que “conversar”, de forma indenpendente, com serviços fornecidos por companhias áreas, locadoras de automóveis e hotéis.

E se algo der errado…

No mundo perfeito, sempre que um cliente resolvesse viajar, poderíamos disparar a compra do bilhete aéreo, a reserva do hotel e do carro de forma paralela. Nosso trabalho seria apenas consolidar as confirmações em nosso “banco de dados” para poder fornecer ao cliente as informações de cada serviço contratado. Mas, o que aconteceria se:

  • Não houvesse hotel disponível para a cidade escolhida pelo nosso cliente?
  • Não houvesse a opção de carro que o cliente precisa?
  • Não houvesse opção viável de voo?

Na prática, provavelmente, a falha de qualquer uma dessas operações implicaria no cancelamento da viagem. Entretanto, perceba que, agora, não temos uma “transação” para dar rollback. A saída empírica é executar uma atividade compensatória (cancelamentos) anulando os efeitos produzidos nos serviços que foram completados com êxito.

No nosso contexto fictício, por exemplo, se tivessemos êxito em reservar o hotel e o carro, mas houvesse uma falha na compra do bilhete aérero, teríamos que “voltar” aos serviços fornecidos pelo hotel e pela locadora de automóveis para cancelar reservas. Para tornar o cenário ainda mais desafiador, precisaríamos entender as implicações destes cancelamentos para evitar multas, por exemplo.

Enfim, Sagas

Em 1987, Hector Garcia-Molina e Kenneth Salem escreveram um paper clássico, onde descrevem a abstração utilizada até hoje pela indústria para resolver o problema que estamos tratando nesse post: Sagas.

De forma simplificada, Sagas são transações de longa duração. No nosso exemplo, quando um cliente dispara a compra de bilhetes aéreos, reserva de hotel e de carro, está iniciando uma Saga!

Saga é uma transação de longa duração que pode ser escrita como uma sequência de transações indepententes e intercambiáveis.

Todas as transações na sequência devem ser completadas com êxito. Caso contrário, ações compensatórias são executadas para desfazer os efeitos da execução parcial.

Em nosso exemplo, a saga “Viagem do Elemar” seria composta por três transações independentes: 1) Compra da Passagem Aérea; 2) Reserva do Hotel e 3) Reserva do Carro.

Cada transação na Saga, tem uma ação compensatória capaz de “desfazer” uma transação parcial bem-sucedida.

Repare que todas as transações da saga estão correlacionadas.

Saga Execution Coordinator (SEC)

Em nossa arquitetura, nosso serviço é o coordenador da execução da Saga.

 

O trabalho do SEC é coordenar a execução da Saga mantendo um log das transações e/ou ações compensatórias. Eis aqui um caso natural para Event-sourcing.

Relação com microsserviços

Por definição, quando trabalhamos com microsserviços, temos desafios semelhantes aos descritos no exemplo que apresentei. Cada microsserviço deve ter seu próprio banco de dados. Logo, não há “fonte única da verdade”.  Abordagens baseadas em Two Phase Commits – 2PC (hoje considerado um anti-pattern) e coreografia de eventos tem sido implementadas mas são consideradas difíceis de manter.

Geralmente, nos projetos em que tenho participado, estamos optando pela implementação do padrão Saga para implementação de transações em microsserviços. Até agora, sem arrependimentos.

Considerações finais

Nesse post, explicamos o padrão Saga. O único desafio que tenho enfrentado é que, semanticamente, a ação compensatória deveria anular o efeito da transação a que está relacionada. Entretanto, isso nem sempre isso é possível. No nosso exemplo, o cancelamento de um bilhete aereo pode ocasionar multa – ou seja, não há ação compensatória explícita. A saída, nesses casos, é,  junto aos especialistas de domínio, tentar definir estratégias (deixando o bilhete aéreo para última parte da Saga, por exemplo).

Entre em contato comigo caso esteja implementando o padrão saga (ou outra alternativa) agora. Posso te ajudar a encurtar alguns caminhos.

5 Comentários
  1. Elemar, mas se o computador trava, como que ele vai executar a função de compensação? O que veio em minha mente é ter um outro serviço que caso a máquina travava(algum outro problema que impedisse de continuar com o processamento), a outra maquina rodava para desfazer o que a outra fez. Eu não entendo bem sobre sagas, mas foi que pensei aqui rsrs.

    1. Junior, sagas podem ser stateful. Em geral, as libraries que lidam com sagas implementam a persistência e, por isso, conseguem se recuperar de falhas.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *