pattern

Outbox Pattern

also called Transactional Outbox

Writing a message to a table in the same database transaction as the state change, then publishing it asynchronously, so the two cannot diverge.

messagingconsistencyintegration

This solves the dual-write problem, which is one of the most common sources of silent data inconsistency in service architectures and one that most teams create without realising it.

The dual write: a service updates its database and then publishes an event. These are two separate systems with no shared transaction. If the process dies between them, the state changed and no event was published — the order exists and nothing downstream knows. Reverse the order and you can publish an event for a change that then fails to commit.

The outbox removes the gap by making the message part of the state change. The event row is inserted in the same local transaction as the business data, so both commit or neither does. A separate process then reads the outbox — by polling or, better, by tailing the transaction log through change data capture — publishes the message, and marks it sent.

The guarantee it provides is at-least-once, not exactly-once: a crash after publishing and before marking will republish. Consumers must therefore be idempotent, which is the correct assumption in any case.

The operational details that decide whether it works: the outbox table needs pruning or it becomes the largest table in the database, and the publisher's lag needs monitoring, because a stalled publisher is invisible from the application's point of view — everything looks committed.