- rtshkmr's digital garden/
- References/
- Architecture Design Basics/
- Pattern Taxonomy/
- Communication & API Design/
- Outbox Pattern/
Outbox Pattern
Table of Contents
🟠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 publishedThe 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 #
| Dimension | Benefit | Cost |
|---|---|---|
| Consistency | Atomic: DB write + event guaranteed | Extra table, extra process |
| Latency | N/A (async anyway) | Slight delay (polling interval or CDC) |
| Complexity | Eliminates dual-write bugs | Need CDC infrastructure or poller |
| Ordering | Preserves write order within partition | Cross-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 #
- microservices.io: Transactional Outbox — Chris Richardson
- Debezium Tutorial — the de facto CDC tool
DDIA 2e Reference #
- Chapter 11: Exactly-once semantics in stream processing
- Chapter 12: Data integration across systems