Updated July 2026. Refreshed for current microservices best practices.
The Dual-Write Problem
When a service saves to its database AND publishes to Kafka, either operation can fail independently. You might persist an order but never publish the event — or publish without saving. The Outbox pattern solves this with a single atomic transaction.
How the Outbox Pattern Works
- Begin database transaction.
- Insert/update business data (e.g., orders table).
- Insert event row into
outboxtable in the same transaction. - Commit transaction — both writes succeed or both roll back.
- Separate relay process polls outbox, publishes to Kafka, marks rows as sent.
Outbox Table Schema
CREATE TABLE outbox (
id UUID PRIMARY KEY,
aggregate_type VARCHAR(100),
aggregate_id VARCHAR(100),
event_type VARCHAR(100),
payload JSONB,
created_at TIMESTAMPTZ DEFAULT NOW(),
published_at TIMESTAMPTZ
);
Relay Worker Best Practices
- Poll in batches with
FOR UPDATE SKIP LOCKEDfor concurrency. - Make Kafka publishes idempotent (use event ID as key).
- Retry with exponential backoff on broker failures.
- Monitor outbox lag (unpublished row count).
Alternatives: Debezium CDC reads database WAL and streams changes to Kafka without polling. Related: Saga Pattern · Kafka Topics Guide
Want live microservices classes? Join Alkademy for instructor-led microservices architecture courses with hands-on projects.
Go deeper with the book: Practical Microservices Design Using CQRS and Event Sourcing by Kindson Munonye.
Kindson Munonye is a software engineer and technical author specializing in microservices, CQRS, event sourcing, and distributed systems. He publishes free step-by-step tutorials and live classes on Alkademy.