Saga Pattern
Gerenciar transações que cruzam múltiplos serviços sem um coordenador ACID central — decompondo a operação em transações locais encadeadas e definindo transações compensatórias para desfazer o que já foi feito em caso de falha.
Intenção
Uma Saga é uma sequência de transações locais, cada uma em um serviço diferente, onde a falha de qualquer etapa dispara transações compensatórias nas etapas anteriores para reverter o efeito. É a resposta prática ao problema de consistência em sistemas distribuídos onde cada serviço tem seu próprio banco de dados.
Em um monolito com banco único, uma transação ACID garante que ou todas as
operações acontecem, ou nenhuma acontece. Em microsserviços, cada serviço isola
seus dados em seu próprio banco — não há como usar um único BEGIN
e COMMIT que abranja todos eles. O Saga Pattern aceita essa
realidade e trabalha com consistência eventual em vez de consistência imediata,
trocando a atomicidade distribuída por uma sequência gerenciada de transações
locais com mecanismo de rollback de negócio.
Problema
Considere um fluxo de e-commerce: o cliente faz um pedido que precisa (1) criar o pedido, (2) reservar estoque no serviço de inventário, (3) processar o pagamento no serviço financeiro e (4) acionar a notificação. Cada um desses passos vive em um serviço com banco próprio.
- Two-Phase Commit (2PC) não escala: o protocolo de commit em duas fases exige que todos os participantes bloqueiem recursos durante a fase de preparação. Em sistemas com dezenas de serviços, isso cria contenção, aumenta a latência e cria dependência de um coordenador central. A maioria dos bancos modernos de microsserviços (DynamoDB, MongoDB, CockroachDB) não suporta 2PC cross-serviço de forma prática.
- Ignorar o problema cria inconsistência silenciosa: se o pagamento falha após a reserva de estoque já ter sido confirmada, o sistema fica com estoque reservado para um pedido que nunca existirá — a menos que haja um mecanismo explícito de compensação.
- Estado parcialmente commitado é inevitável: entre a confirmação do pedido e a confirmação do pagamento, o sistema passa por estados intermediários que precisam ser projetados e comunicados ao usuário de forma coerente.
Como funciona
A estrutura de uma Saga
Cada passo da saga executa uma transação local em um único banco de dados e publica um evento ou envia uma mensagem para o próximo participante. Se um passo falha, a saga executa transações compensatórias nos passos anteriores, na ordem inversa.
Transações compensatórias não são rollbacks técnicos — são operações de negócio que semanticamente desfazem o efeito. "Cancelar reserva de estoque" não apaga a linha do banco: cria uma nova operação que libera as unidades reservadas. Isso é fundamental porque, ao contrário de um rollback de banco, a compensação acontece depois que a transação original já foi commitada e potencialmente vista por outros sistemas.
Coreografia (Choreography)
Cada serviço publica eventos após completar sua transação local e reage a eventos publicados por outros serviços. Não há coordenador central — o fluxo emerge da cadeia de reações entre os participantes.
Serviço Pedido Serviço Estoque Serviço Pagamento
│ │ │
│── PedidoCriado ─────────────►│ │
│ │── EstoqueReservado ──────►│
│ │ │── PagamentoProcessado
│ │ │
│ [se falha] [se falha]
│◄── ReservaFalhou ────────────│ │◄── PagamentoFalhou ─┘
│ (cancela pedido) │ (cancela reserva)
Vantagem: simples de implementar para poucos serviços, sem ponto único de falha. Desvantagem: o fluxo de uma saga está espalhado por múltiplos serviços — entender o comportamento completo exige correlacionar logs e eventos de vários sistemas simultaneamente.
Orquestração (Orchestration)
Um Saga Orchestrator — um processo ou serviço dedicado — coordena os participantes explicitamente: chama cada serviço em sequência, aguarda a resposta e toma a decisão de avançar ou acionar compensações em caso de falha. O fluxo inteiro vive em um único lugar.
Comparativo: Coreografia vs Orquestração
COREOGRAFIA ORQUESTRAÇÃO
─────────────────────────────────────────────────────────
Coordenador Nenhum Saga Orchestrator
Acoplamento Baixo (via eventos) Médio (ao orquestrador)
Observabilidade Difícil Centralizada no orch.
Complexidade Baixa (poucos svcs) Gerenciável (escala)
Ponto único falha Não Orquestrador (mitigar)
Fluxo explícito Não Sim
─────────────────────────────────────────────────────────
Indicado para 2–3 serviços 4+ serviços
fluxos simples fluxos complexos
Quando usar
- Microsserviços com bancos isolados: quando cada serviço tem seu próprio banco e não é possível — ou desejável — compartilhar uma conexão de banco transacional. Saga é a alternativa prática ao 2PC nesse contexto.
- Transações de negócio multi-serviço: fluxos como checkout (pedido + estoque + pagamento + notificação), onboarding de usuário (cadastro + envio de email + criação de conta de cobrança) ou reservas (voo + hotel + carro).
- Consistência eventual aceitável: quando o negócio tolera estados intermediários temporários — o pedido existe mas o pagamento ainda está sendo processado — e a compensação resolve o caso de falha de forma aceitável.
Quando evitar
- Consistência forte obrigatória: operações financeiras que exigem atomicidade real (débito e crédito devem ser atômicos) são melhor resolvidas dentro de um único serviço com banco transacional ou com two-phase commit suportado pelo banco.
- Número muito alto de participantes: sagas com 10+ passos criam cascatas de compensação complexas e difíceis de testar. Nesses casos, reavaliar a granularidade dos serviços pode ser mais eficaz.
- Equipe sem maturidade em sistemas distribuídos: sagas requerem atenção a idempotência, rastreabilidade e tratamento de falha em compensações. Sem essa maturidade, o resultado costuma ser pior do que um monolito transacional.
Prós e contras
Prós
- Permite consistência eventual entre microsserviços sem bloquear recursos (sem locks globais).
- Cada serviço mantém sua autonomia e banco próprio — sem acoplamento de infraestrutura.
- Orquestração centraliza o fluxo, facilitando observabilidade, testes e rastreamento de status da saga.
- Escala melhor que 2PC em sistemas com muitos participantes e alto volume de transações.
Contras
- Estado intermediário visível: o sistema fica em estados parcialmente commitados entre os passos, o que precisa ser comunicado na UX.
- Compensação é mais difícil do que rollback: transações compensatórias precisam ser projetadas, implementadas, testadas e monitoradas separadamente.
- Debugging complexo: rastrear o estado de uma saga distribuída por múltiplos serviços requer correlation ID, tracing distribuído e atenção ao log.
- Não substitui ACID onde consistência imediata é exigida — é uma troca consciente, não uma solução universal.
Armadilhas comuns
1. Compensação não idempotente
Se o serviço de compensação falha após executar a operação mas antes de confirmar o sucesso, o orquestrador vai retentar. Sem idempotência, a compensação é executada duas vezes — o pagamento é estornado duas vezes, o estoque é liberado em dobro. Toda transação compensatória deve ser projetada para produzir o mesmo resultado quando executada múltiplas vezes.
Prática: use uma chave de idempotência (ID da saga + ID do passo) nas transações compensatórias. Antes de executar, verifique se a compensação para aquela chave já foi aplicada.
2. Estado intermediário exposto ao usuário
Entre o passo 1 (pedido criado) e o passo 3 (pagamento confirmado), o pedido existe no sistema mas não está completo. O usuário pode ver um pedido "aguardando confirmação" que depois é cancelado por falha no pagamento. A UX precisa comunicar esses estados intermediários de forma clara — um pedido em processamento não é um pedido confirmado.
3. Coreografia sem rastreabilidade
Em coreografia com muitos serviços, entender o estado atual de uma saga exige correlacionar eventos de todos os participantes. Sem correlation ID presente em todos os eventos e logs, e sem tracing distribuído (OpenTelemetry, Jaeger), um suporte para investigar uma saga com falha pode levar horas.
4. Compensação que falha
E se a própria transação compensatória falhar? Esse é o caso mais difícil. A saga entra em estado inconsistente que requer intervenção. A solução é ter retry automático com backoff para as compensações e, após exaustão das tentativas, alertar a equipe de operações com contexto suficiente para intervenção manual. Nunca ignore silenciosamente uma falha de compensação.
Arquiteturas e padrões relacionados
O Outbox Pattern é frequentemente combinado com Sagas: cada passo da saga salva o resultado no banco e insere o próximo comando/evento na tabela outbox dentro da mesma transação local, garantindo que o evento não seja perdido mesmo que o processo falhe após o commit.
Idempotência é pré-requisito de Sagas: tanto os passos normais quanto as compensações serão retentados em caso de falha de rede, e precisam produzir o mesmo resultado em todas as tentativas.
CQRS e Event Sourcing complementam Sagas: o evento de domínio é tanto o mecanismo de comunicação entre participantes (coreografia) quanto a fonte de verdade do estado da saga (o estado pode ser reconstruído a partir do log de eventos).