Pular para o conteúdo

apps/patterns/outbox-pattern

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

Outbox Pattern

Garante que a persistência de dados e a publicação de mensagens ocorram atomicamente, sem depender de transações distribuídas. O serviço grava no banco de dados e na tabela outbox na mesma transação local; um relay separado publica as mensagens pendentes para o broker.

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

Cenário

Um serviço de pagamentos grava o PIX e precisa avisar o serviço de notificação. Publicar no broker depois do INSERT é dual write: o processo cai entre os dois e o evento some. Com o Outbox, o evento entra na mesma transação do pagamento, e um relay separado o entrega a um Redis Stream — que, ao contrário do Pub/Sub, guarda o evento até o consumidor ler.

Planta

Sequência
8/8
loop["a cada 1 s"]transação única — ou tudo ou nadaBEGIN, INSERT payments, INSERT outbox_messages (pending), COMMIT1reaper: publishing com claimed_at vencido volta a pending2claim: UPDATE ... publishing, claimed_at (FOR UPDATE SKIP LOCKED)3XADD com o eventId4UPDATE status = published5XREADGROUP6SET NX pelo eventId — duplicata é só confirmada7XACK8Serviço de pagamentosPostgreSQLRelayRedis Stream payment_eventsConsumidor (grupo payment_notifier)
8

8 passos — reproduza para seguir o fluxo

Como funciona

6 passos

Evento gravado na transação do dado.

Esconde cada passo: lembre antes de tocar para revelar.

  1. 01

    paymentProcess grava o pagamento e a linha em outbox_messages (pending) na mesma transação

  2. 02

    O relay reivindica um lote com FOR UPDATE SKIP LOCKED, gravando publishing e claimed_at — vários relays pegam lotes disjuntos

  3. 03

    Publica cada evento no stream payment_events (XADD, com o eventId) e marca published

  4. 04

    O reaper devolve a pending toda linha publishing com claimed_at além do lease — o relay que a reivindicou morreu antes de marcar

  5. 05

    A entrega é at-least-onceO consumidor lê por consumer group e deduplica pelo eventId com SET NX EX antes de notificar

  6. 06

    O demo simula o relay caindo depois de publicar: o evento sai de novo e o consumidor o reconhece como duplicado

apps/patterns/outbox-pattern

8 arquivos

sql/

  • 01_schema.sqlschemaTabelas payments e outbox_messages (com claimed_at e índices parciais)

src/

  • service_payment.tspaymentProcess — pagamento e evento na mesma transação
  • relay_outbox.tsClaim com SKIP LOCKED, XADD no stream, marcação e reaper por lease
  • consumer_payment.tsConsumer group no stream com deduplicação por eventId
  • pool_bank.tstransactionExecute e conversão de centavos para NUMERIC
  • config_payment.tsLê e valida a configuração no boot
  • demo.tsdemoCria pagamentos, sobe relay e consumidor e simula a queda do relay
  • outbox_payment.test.tstesteIntegração com PostgreSQL e Redis reais: atomicidade, lotes disjuntos, lease e duplicata

Executar · com Docker

  1. docker compose up -d# sobe PostgreSQL · Redis
  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
PostgreSQLRedis

Por que se relacionam

Estados da outbox

Status Significado
pending Aguardando o relay
publishing Reivindicado por um relay, com claimed_at
published Publicado no stream

Teste rápido

Qual é o próximo passo depois de Outbox Pattern?

Próximo projeto · Padrões FundamentaisPG Pool Manager
Esc

↑ ↓ navegarEnter abrir191 resultados