pattern

Outbox Pattern

also called Transactional Outbox

Writing an outgoing message into a table in the same transaction as the business change, and relaying it to the broker separately, so the two cannot diverge.

messagingconsistencydual-write

The problem it solves is the dual write: committing a database change and publishing an event are two independent operations with no atomicity between them. Crash in between and the record exists with no event; roll back after publishing and consumers act on something that does not exist.

The outbox makes it one write. The event row and the business row commit together, atomically. A relay process — usually change data capture tailing the write-ahead log, sometimes a poller — reads unpublished rows, publishes them and marks them sent.

Consequences to design for: delivery becomes at-least-once, because the relay can publish and crash before marking, so consumers must be idempotent. Ordering is preserved per partition key if the relay respects it, which CDC does naturally and a naive concurrent poller does not. And sent rows need deletion or partitioning, or the outbox becomes the largest table in the database.

The inverse — the inbox pattern — records processed message IDs on the consumer side to make deduplication explicit rather than incidental.