Modelo SAGA: como garantir consistência em transações distribuídas

O modelo SAGA é um padrão arquitetural criado para lidar com um dos maiores desafios dos sistemas distribuídos: manter a consistência dos dados em transações que atravessam múltiplos microserviços e bancos de dados. Ele é especialmente útil em cenários onde as operações são longas, assíncronas e não podem ser tratadas como uma única transação atômica tradicional.

O nome “Saga” vem da ideia de uma jornada dividida em capítulos. Em termos técnicos, isso significa quebrar uma transação complexa em várias etapas menores, executadas de forma sequencial e distribuída. Cada etapa é responsável por uma operação específica e, caso algo dê errado no meio do caminho, existem ações compensatórias capazes de desfazer o que já foi executado, restaurando a consistência do sistema.

Vale deixar claro desde o início: o foco do modelo SAGA é confiabilidade e consistência, não desempenho. Em muitos casos, ele aceita pagar um custo extra de latência para evitar estados inválidos ou dados corrompidos.

A origem do padrão SAGA

O SAGA Pattern não nasceu com os microserviços. Ele foi apresentado em 1987 por Hector Garcia-Molina e Kenneth Salem, em um artigo acadêmico da Universidade de Princeton, com o objetivo de resolver o problema das chamadas Long Lived Transactions. Na época, já existia a necessidade de lidar com processos que duravam mais do que uma transação tradicional de banco de dados, sem bloquear recursos por longos períodos.

A solução proposta foi dividir essas transações longas em várias transações menores, cada uma com sua própria lógica de confirmação e reversão. Décadas depois, com a popularização de arquiteturas distribuídas e microserviços, esse conceito voltou com força total, agora como uma peça-chave para garantir consistência em sistemas modernos.

O desafio das transações distribuídas

Uma transação distribuída envolve múltiplos serviços independentes, cada um com seu próprio banco de dados. Para que tudo funcione corretamente, todos precisam executar suas operações com sucesso. Basta um falhar para o sistema correr o risco de entrar em um estado inconsistente.

Um exemplo clássico é o fluxo de um pedido em um e-commerce. O pedido pode envolver serviços de pedidos, pagamentos, estoque, entregas e notificações. Se o pagamento for aprovado, mas o estoque falhar, o sistema precisa desfazer o pagamento ou executar algum tipo de compensação. Sem um mecanismo adequado, o resultado pode ser um pagamento confirmado para um pedido que nunca será entregue.

O modelo SAGA existe justamente para lidar com esse tipo de situação, garantindo que falhas intermediárias não deixem o sistema em um estado quebrado.

Transações longas e por que elas complicam tudo

Nem toda transação termina em segundos. Muitos processos levam minutos, horas ou até dias para serem concluídos. Parcelamentos, fechamentos de fatura, agendamentos financeiros, consolidações de uso e fluxos de aprovação são exemplos comuns.

Manter controle e visibilidade sobre esse tipo de transação longa é difícil. É preciso saber em que etapa ela está, quais passos já foram concluídos e o que fazer se algo falhar depois de muito tempo. O SAGA resolve isso ao dividir o processo em etapas menores, cada uma com responsabilidade clara e com capacidade de compensação.

Como funciona uma transação SAGA

Em essência, uma SAGA é uma sequência de transações locais, cada uma executada por um microserviço. Essas transações não são atômicas entre si, mas o conjunto delas representa uma operação lógica única.

Se todas as etapas forem concluídas com sucesso, a saga termina normalmente. Se alguma falhar, entram em cena as transações compensatórias, que desfazem os efeitos das etapas anteriores, mantendo o sistema consistente.

Esse modelo evita bloqueios longos e elimina a dependência de mecanismos pesados como o Two-Phase Commit em sua forma clássica, que costuma gerar gargalos em ambientes distribuídos.

SAGA orquestrada vs SAGA coreografada

Existem duas formas principais de implementar o padrão SAGA.

No modelo orquestrado, há um componente central chamado orquestrador. Ele conhece todo o fluxo da transação, dispara comandos para os microserviços, aguarda respostas e decide qual é o próximo passo. Em caso de falha, ele coordena as compensações. Esse modelo centraliza a complexidade, facilita monitoramento e rastreabilidade, mas adiciona um ponto extra de responsabilidade à arquitetura.

Já no modelo coreografado, não existe um coordenador central. Cada microserviço sabe qual evento consumir e qual evento publicar após concluir sua parte. A saga avança por meio de eventos, formando uma espécie de dança entre os serviços. É um modelo mais descentralizado, porém mais difícil de observar, debugar e evoluir em fluxos complexos.

Máquinas de estado e ciclo de vida da SAGA

Um ponto crítico em qualquer implementação de SAGA é o controle de estado. Normalmente, isso é feito com uma máquina de estados, que representa em que etapa a saga se encontra e quais transições são válidas a partir dali.

Estados como “pedido criado”, “estoque reservado”, “pagamento confirmado” ou “entrega iniciada” ajudam a entender o progresso da transação. Eventos recebidos dos microserviços disparam transições entre esses estados, além de ações associadas, como chamar outro serviço ou executar uma compensação.

Esse controle permite rastrear sagas em andamento, identificar falhas, aplicar retentativas e gerar métricas confiáveis sobre o comportamento do sistema.

Ações compensatórias e rollback distribuído

No modelo SAGA, toda ação importante deve ter uma ação de compensação correspondente. Reservar estoque deve ter um mecanismo para liberar estoque. Cobrar um pedido deve ter um processo de estorno. Esse pareamento é essencial para garantir que o sistema consiga voltar a um estado consistente em caso de erro.

Em arquiteturas orientadas a eventos, é comum existir um fluxo específico de compensação, onde cada serviço recebe o sinal para desfazer suas operações, se necessário.

O problema do Dual Write

Um dos maiores desafios práticos do SAGA é o chamado Dual Write. Ele acontece quando um serviço precisa gravar dados em dois lugares diferentes, como um banco de dados e um broker de mensagens. Se um falhar e o outro não, o sistema entra em um estado inconsistente.

Esse problema pode ocorrer tanto em sagas coreografadas quanto orquestradas. Para mitigar o risco, padrões como Outbox Pattern e Change Data Capture são amplamente utilizados. A ideia é garantir que a gravação dos dados e a publicação do evento façam parte do mesmo contexto transacional, reduzindo as chances de falha parcial.

SAGA e Two-Phase Commit

Embora o SAGA seja frequentemente associado a arquiteturas assíncronas, ele pode coexistir com abordagens síncronas. Em alguns cenários críticos, o Two-Phase Commit ainda pode ser usado para garantir um nível maior de atomicidade, especialmente quando o cliente precisa de uma resposta imediata.

O problema é que o 2PC não escala bem em ambientes de alta concorrência, pois mantém conexões abertas e bloqueios por mais tempo. Por isso, ele deve ser usado com cuidado e apenas quando realmente necessário.

Reinicialização e idempotência

Falhas acontecem, mesmo com todos os cuidados. Por isso, sistemas baseados em SAGA precisam ser idempotentes. Um mesmo comando pode ser recebido mais de uma vez sem causar efeitos colaterais indesejados.

Isso permite reiniciar sagas, retomar etapas que ficaram pendentes e recuperar o sistema após falhas parciais, sem gerar inconsistências adicionais.

O modelo SAGA é uma das soluções mais importantes para lidar com transações distribuídas em arquiteturas de microserviços. Ele troca atomicidade forte por consistência eventual, mas oferece mecanismos claros de controle, compensação e recuperação.

Quando bem implementado, o SAGA permite construir sistemas resilientes, escaláveis e preparados para falhas, algo essencial em ambientes distribuídos modernos.