Saga Pattern

Como lidar com transações distribuídas entre microsserviços usando coreografia ou orquestração com compensações.

Que problema resolve

Em um monólito, uma transação de banco de dados pode abranger múltiplas tabelas com um simples BEGIN → COMMIT → ROLLBACK. Mas em uma arquitetura de microsserviços, cada serviço tem seu próprio banco de dados. Não existe uma transação global capaz de fazer rollback em 5 bancos de dados diferentes se algo falhar no passo 3.

O Saga Pattern resolve esse problema dividindo uma transação distribuída em uma sequência de transações locais, onde cada passo tem uma ação compensatória que desfaz seu efeito caso um passo posterior falhe.

Exemplo do problema

Imagine um fluxo de compra em um e-commerce:

  1. Serviço de Pedidos: Criar pedido
  2. Serviço de Estoque: Reservar estoque
  3. Serviço de Pagamentos: Cobrar o cliente
  4. Serviço de Envios: Agendar envio

Se o pagamento falhar no passo 3, você precisa desfazer a reserva de estoque (passo 2) e cancelar o pedido (passo 1). Sem um padrão definido, isso se torna um caos de chamadas manuais e estados inconsistentes.

Por que não usar transações distribuídas (2PC)

O protocolo Two-Phase Commit (2PC) parece a solução óbvia, mas tem problemas sérios em microsserviços:

Aspecto2PCSaga
DisponibilidadeTodos os participantes devem estar disponíveis simultaneamenteCada passo é independente
LatênciaBloqueios durante toda a transaçãoCada passo libera recursos imediatamente
EscalabilidadeOs locks distribuídos limitam o throughputSem locks, cada serviço escala de forma independente
AcoplamentoRequer um coordenador que conheça a todosOs serviços se comunicam por eventos
RecuperaçãoSe o coordenador falha, os participantes ficam bloqueadosAs compensações são executadas de forma assíncrona

Como funciona

Uma saga é uma sequência de transações locais onde:

  • Cada transação atualiza dados dentro de um único serviço
  • Cada transação publica um evento ou mensagem que dispara o próximo
  • Se uma transação falha, executam-se compensações na ordem inversa
Fluxo bem-sucedido:
  T1 → T2 → T3 → T4 ✅

Fluxo com falha em T3:
  T1 → T2 → T3 ❌ → C2 → C1
  (C = compensação)

Existem duas estratégias principais para coordenar uma saga:

Coreografia (Choreography)

Cada serviço escuta eventos e decide o que fazer. Não há um coordenador central.

┌──────────┐    PedidoCriado     ┌──────────────┐
│ Pedidos  │ ──────────────────► │   Estoque    │
└──────────┘                     └──────┬───────┘

                                 EstoqueReservado


                                 ┌──────────────┐
                                 │  Pagamentos  │
                                 └──────┬───────┘

                                 PagamentoAprovado


                                 ┌──────────────┐
                                 │    Envios     │
                                 └──────────────┘

Vantagens: Sem ponto único de falha, serviços desacoplados, fácil adicionar participantes. Desvantagens: Difícil entender o fluxo completo, risco de ciclos, difícil depurar.

Orquestração (Orchestration)

Um componente central coordena toda a saga. Ele sabe quais passos executar e quais compensações aplicar.

                    ┌──────────────────┐
                    │   Orquestrador   │
                    │    de Saga       │
                    └──┬───┬───┬───┬──┘
                       │   │   │   │
            Criar      │   │   │   │  Agendar
            pedido     │   │   │   │  envio
                       ▼   │   │   ▼
                 ┌────────┐│   │┌────────┐
                 │Pedidos ││   ││Envios  │
                 └────────┘│   │└────────┘
                    Reservar   │  Cobrar
                    estoque│   │  pagamento
                           ▼   ▼
                    ┌────────┐┌────────┐
                    │Estoque ││Pagam.  │
                    └────────┘└────────┘

Vantagens: Fluxo centralizado, controle explícito das compensações, fácil depurar. Desvantagens: Potencial ponto único de falha, risco de God Service, acoplamento.

Quando escolher cada estratégia

CritérioCoreografiaOrquestração
Número de passos2-4 passos simples5+ passos ou fluxos complexos
VisibilidadeAceitável se for simplesNecessária para fluxos críticos
TimesAutônomos e independentesTime central gerencia o fluxo
CompensaçõesSimples e diretasComplexas com lógica condicional
MonitoramentoRastreabilidade distribuídaDashboard centralizado

Transações compensatórias

As compensações não são simplesmente um “undo”. São ações que semanticamente revertem o efeito de uma transação anterior:

TransaçãoCompensação
Criar pedidoCancelar pedido (mudar o estado, não excluir)
Reservar estoqueLiberar o estoque reservado
Cobrar pagamentoEmitir reembolso
Agendar envioCancelar o agendamento do envio

Pontos-chave sobre compensações:

  • Não são rollbacks: Não apagam dados, criam novos registros que revertem o efeito
  • Devem ser idempotentes: Executá-las várias vezes produz o mesmo resultado
  • Podem falhar: Você precisa de retentativas para compensações que falharam
  • Mantêm auditoria: Tudo fica registrado (ação original e compensação)

Exemplo prático: Saga de compra com orquestração

Caminho feliz

Orquestrador        Pedidos    Estoque    Pagamentos   Envios
    │                   │           │           │          │
    │── CriarPedido ───►│           │           │          │
    │◄── PedidoCriado ──│           │           │          │
    │                   │           │           │          │
    │── ReservarEstoque ────────────►│           │          │
    │◄── EstoqueReservado ──────────│           │          │
    │                   │           │           │          │
    │── CobrarPagamento ────────────────────────►│          │
    │◄── PagamentoAprovado ─────────────────────│          │
    │                   │           │           │          │
    │── AgendarEnvio ───────────────────────────────────────►│
    │◄── EnvioAgendado ─────────────────────────────────────│
    │                   │           │           │          │
    ✅ Saga concluída

Falha no pagamento (com compensações)

Orquestrador        Pedidos    Estoque    Pagamentos
    │                   │           │           │
    │── CriarPedido ───►│           │           │
    │◄── PedidoCriado ──│           │           │
    │                   │           │           │
    │── ReservarEstoque ────────────►│           │
    │◄── EstoqueReservado ──────────│           │
    │                   │           │           │
    │── CobrarPagamento ────────────────────────►│
    │◄── PagamentoRecusado ─────────────────────│  ❌
    │                   │           │           │
    │  ┌── COMPENSAÇÕES ────────────────────┐  │
    │  │── LiberarEstoque ──────────►│      │  │
    │  │◄── EstoqueLiberado ────────│      │  │
    │  │── CancelarPedido ──►│       │      │  │
    │  │◄── PedidoCancelado ─│       │      │  │
    │  └────────────────────────────────────┘  │
             │           │           │
    ❌ Saga compensada

Estado persistido do orquestrador

{
  "sagaId": "saga-12345",
  "tipo": "CompraCompleta",
  "estado": "COMPENSANDO",
  "pasoActual": "LiberarStock",
  "datos": {
    "pedidoId": "ped-001",
    "clienteId": "cli-789",
    "monto": 150.00
  },
  "historial": [
    { "paso": "CrearPedido", "estado": "COMPLETADO" },
    { "paso": "ReservarStock", "estado": "COMPLETADO" },
    { "paso": "CobrarPago", "estado": "FALLIDO", "motivo": "Fondos insuficientes" },
    { "paso": "LiberarStock", "estado": "COMPENSANDO" }
  ]
}

Vantagens

  • Consistência eventual: Garante que o sistema chegará a um estado consistente
  • Autonomia dos serviços: Cada serviço mantém seu próprio banco de dados e transações locais
  • Resiliência: Se um passo falha, as compensações restauram a consistência
  • Escalabilidade: Não requer locks distribuídos nem coordenação síncrona
  • Auditoria natural: Cada passo e compensação fica registrado como evento
  • Flexibilidade: É possível adicionar ou modificar passos sem afetar todo o fluxo

Trade-offs / Desvantagens

  • Complexidade: Projetar compensações corretas para cada passo não é trivial
  • Consistência eventual: O sistema pode estar temporariamente inconsistente
  • Difícil de depurar: Rastrear uma saga requer boas ferramentas de observabilidade
  • Compensações imperfeitas: Nem sempre é possível reverter completamente uma ação
  • Ordem de execução: Na coreografia, garantir a ordem correta é complexo
  • Testing: Testar todos os caminhos possíveis exige esforço significativo
  • Janela de inconsistência: Durante a execução, os dados podem estar em estado parcial

Quando usar

  • Transações que cruzam múltiplos microsserviços com bancos de dados independentes
  • Fluxos de negócio de longa duração (minutos, horas ou dias)
  • Sistemas onde a consistência eventual é aceitável
  • Quando você precisa de auditoria completa de cada passo do processo
  • Fluxos com passos que podem falhar e precisam de compensação definida
  • Sistemas event-driven onde os serviços já se comunicam por eventos

Quando evitar

  • Operações que exigem consistência forte e imediata (ACID)
  • Fluxos simples que envolvem apenas 2 serviços (pode ser overkill)
  • Quando as compensações são impossíveis ou não fazem sentido
  • Sistemas onde a janela de inconsistência não é aceitável
  • Times sem experiência em sistemas distribuídos
  • Quando um monólito com transações locais resolve o problema

Ferramentas e implementações comuns

CategoriaOpções
OrquestradoresTemporal, Camunda, AWS Step Functions, Netflix Conductor
MensageriaApache Kafka, RabbitMQ, Amazon SQS/SNS, Azure Service Bus
FrameworksAxon Framework (Java), MassTransit (.NET), Eventuate Tram
ObservabilidadeJaeger, Zipkin, OpenTelemetry (tracing distribuído)
Estado da sagaPostgreSQL, Redis, DynamoDB

Relação com outros padrões

A saga raramente aparece sozinha. Ela se apoia em um ecossistema de padrões complementares:

┌─────────────────────────────────────────────────┐
│              Ecossistema da Saga                 │
│                                                  │
│  ┌──────────┐    publica     ┌──────────────┐   │
│  │  Saga    │ ──────────────►│   Outbox     │   │
│  │ (passos) │    eventos     │  (confiável) │   │
│  └────┬─────┘                └──────────────┘   │
│       │                                          │
│       │ cada passo é                             │
│       │ idempotente                              │
│       ▼                                          │
│  ┌──────────────┐     protege    ┌───────────┐  │
│  │ Idempotency  │ ◄────────────►│  Circuit   │  │
│  │   Pattern    │   chamadas     │  Breaker   │  │
│  └──────────────┘               └───────────┘  │
│                                                  │
│  ┌──────────┐     retentativas  ┌───────────┐  │
│  │  Retry   │ ◄────────────────►│  Timeout  │  │
│  │ Pattern  │   com limite     │  Pattern  │  │
│  └──────────┘                   └───────────┘  │
└─────────────────────────────────────────────────┘
  • Outbox Pattern: Garante que os eventos de cada passo sejam publicados de forma confiável
  • Idempotency Pattern: Cada passo e compensação deve ser idempotente para retentativas seguras
  • Circuit Breaker: Protege o orquestrador quando um serviço participante está fora do ar
  • Retry Pattern: Retenta passos que falharam com backoff antes de acionar compensações
  • Timeout Pattern: Define um limite de tempo para cada passo antes de considerá-lo falho
  • Event-Driven Architecture: A saga é construída sobre um sistema de eventos
  • DDD: Os bounded contexts definem os limites de cada participante

Erros comuns

ErroConsequênciaSolução
Compensações não idempotentesDuplicação de reembolsosUsar IDs de idempotência
Não persistir o estado da sagaSagas perdidas após reinícioSalvar o estado em BD durável
Ignorar falhas nas compensaçõesEstado inconsistente permanenteRetentativas e dead-letter queue
Saga com passos demaisComplexidade ingerenciávelDividir em sub-sagas
Não monitorar sagas ativasSagas travadas sem detecçãoDashboard com alertas

Próximos passos

Uma vez que você domina a saga, explore o Idempotency Pattern para garantir que os passos da saga sejam seguros diante de retentativas.