Skip to main content
  1. References/
  2. Architecture Design Basics/
  3. Pattern Taxonomy/
  4. Communication & API Design/

Outbox Pattern

·· 206 words· 1 min

🟠 P1 — solves the dual-write problem; critical for exactly-once in event-driven systems

Problem #

You need to update a database AND publish a message atomically. If you write to the DB then publish to the queue, the publish might fail (message lost). If you publish first, the DB write might fail (inconsistent state). This is the dual-write problem.

Mechanism #

Service writes:
  1. BEGIN TRANSACTION
  2.   INSERT INTO orders (...)
  3.   INSERT INTO outbox (event_type, payload, published=false)
  4. COMMIT

Separate process (Change Data Capture or Polling):
  1. Read unpublished rows from outbox table
  2. Publish to message broker
  3. Mark rows as published

The key insight: the database transaction guarantees atomicity between the domain write and the event record. The outbox table is a reliable queue inside your database.

Key Trade-offs #

DimensionBenefitCost
ConsistencyAtomic: DB write + event guaranteedExtra table, extra process
LatencyN/A (async anyway)Slight delay (polling interval or CDC)
ComplexityEliminates dual-write bugsNeed CDC infrastructure or poller
OrderingPreserves write order within partitionCross-partition ordering not guaranteed

Instinct #

Use the Outbox Pattern whenever you have a dual-write. It’s the standard solution. The implementation choice is between polling (simpler, higher latency) and Change Data Capture (Debezium, lower latency, more infrastructure). For most systems, CDC is worth the setup cost.

Reference #

DDIA 2e Reference #

  • Chapter 11: Exactly-once semantics in stream processing
  • Chapter 12: Data integration across systems