Pular para o conteúdo

apps/patterns/saga-pattern

028 · Padrões Fundamentais · ≈ 6 min de estudo

Saga Pattern

Troca a transação ACID distribuída por uma sequência de transações locais, cada uma com uma compensação: se uma etapa falha, as já concluídas são desfeitas na ordem inversa por operações de negócio, que ficam no histórico. Use quando uma operação atravessa serviços com bancos próprios e não cabe num BEGIN...COMMIT.

passos
6
arquivos
8
teste
1
tecnologias
5
Infraestrutura realTypeScriptBunElysiaPostgreSQLDocker
Baixar cartão

Cenário

Um banco digital faz transferência internacional em três etapas: débito em reais, câmbio para dólar e envio SWIFT. R$ 2.000,00 para um beneficiário liberado passa por tudo. R$ 3.000,00 para um beneficiário sancionado falha no SWIFT: câmbio e débito são desfeitos e o saldo volta ao anterior. R$ 50.000,00 falha já no débito, sem nada a compensar.

Planta

Sequência
10/10
account.debit started1debita R$ 3.000,002fx.convert started3converte BRL para USD4swift.send started5envia ao beneficiário6recusado pelo compliance7estorna o câmbio (compensação)8devolve o débito (compensação)9compensated, na ordem inversa10Coordenador da sagasaga_logDébito (accounts)Câmbio (fx_conversions)SWIFT (irreversível)
10

10 passos — reproduza para seguir o fluxo

Como funciona

6 passos

Passos locais com compensação reversa.

Esconde cada passo: lembre antes de tocar para revelar.

  1. 01

    Saga log antes da etapasaga_log recebe started antes de cada etapa e o resultado depois; um coordenador que cai no meio tem de onde retomar

  2. 02

    Compensação inversaFalhou a etapa N, compensa N−1 até 1; a etapa que falhou nunca é compensada

  3. 03

    Irreversível por últimoO envio SWIFT não tem compensação e só roda depois que débito e câmbio deram certo

  4. 04

    IdempotênciaCada etapa grava uma linha por saga_id (ON CONFLICT DO NOTHING); a compensação só age em status = 'debited' ou 'converted', então compensar duas vezes devolve o dinheiro uma vez

  5. 05

    Dead letterCompensação que falha vai para saga_dead_letters com alerta, e as demais compensações continuam

  6. 06

    Débito sob lockO saldo é conferido com FOR UPDATE dentro da transação local do débito

Trade-offs

O que se ganha, o que se paga

3 vantagenscada ganho tem um preço3 custos

Vantagens

  • Sem lock distribuído: cada etapa confirma sozinha

  • Recovery pelo saga_log

  • Compensação fica no histórico, auditável

Custos

  • Janela em que o débito existe e o envio ainda não

  • Toda etapa precisa de compensação idempotente

  • Compensação que falha exige intervenção manual

apps/patterns/saga-pattern

8 arquivos

sql/

  • 01_schema.sqlschemaContas, saga_log, tabelas de cada etapa e saga_dead_letters

src/

  • saga_transfer.tsCoordenador: log antes da etapa, compensação inversa e dead letter
  • step_transfer.tsDébito, câmbio e SWIFT, com as compensações idempotentes
  • pool_saga.tsPool único e transação com rollback e release garantidos
  • config_saga.tsLê e valida o ambiente no boot
  • saga_transfer.test.tstesteIntegração contra PostgreSQL real: log, compensação inversa, fronteira do saldo, idempotência e dead letter
  • demo.tsdemoTrês transferências internacionais

./

  • docker.shinfraSobe o PostgreSQL com o schema

Executar · com Docker

  1. ./docker.sh up# sobe PostgreSQL
  2. cp .env.example .env# variáveis de ambiente
  3. bun install# dependências
  4. bun run demo# roda o cenário

Testes: bun run test.

Requisitos
BunDocker
Sobe junto
PostgreSQL
Esc

↑ ↓ navegarEnter abrir191 resultados