The Transactional Outbox Pattern: Reliable Event Publishing (opens in new tab)

You’ve built a microservice that saves orders to a database and publishes events to Kafka. Everything works great—until Kafka goes down for five minutes. Now you have orders in your database but no events published. Your downstream services never learn about these orders. You have inconsistent state.

This is the dual-write problem, and the Transactional Outbox pattern solves it elegantly. Chris Richardson documented this pattern in his Microservices.io pattern catalog, and it’s become a foundational technique for reliable event-driven systems.

A Pattern I’ve Encountered Throughout My Career

Before we dive into implementation, I want to share how I’ve encountered variations of this pattern throughout my career—often before it had a formal name.

Early 2000s: Oracle AQ and Stored Procedures

Early in my career, I worked on projects that needed to track usage data. The process involved opening a ticket to have a DBA write a stored procedure that would insert XML into a database table. On the server side, we’d insert tracking data into this table, and then database triggers would fire events to Oracle Advanced Queuing (AQ) for downstream processing. We didn’t call it an “outbox”—it was just “the tracking table”—but it was exactly this pattern: write to a table atomically with business data, then a separate mechanism handles delivery.

The RabbitMQ Incident: Learning the Hard Way

Later, while building a system that ingested webhooks from third-party services, we published directly to RabbitMQ. It worked great until RabbitMQ went down during a traffic spike. We lost webhooks—events that our partners expected us to process. The fix? We built a simple journal on the HTTP ingestion servers that persisted incoming webhooks to local SQLite files, then a background process that would read the journal and push records to RabbitMQ once it recovered. Classic outbox, implemented under pressure at 2 AM.

RabbitMQ Sidecars

On another project, each web service ran a RabbitMQ “sidecar”—a local broker instance that shoveled messages to a central broker. The application wrote to the local broker (fast, always available), and the shovel handled the unreliable network to the central cluster. This is essentially an outbox where the “table” is a local message queue.

CDC to Kafka

More recently, I’ve worked on systems where synchronous REST APIs buffer events to a database table, and Debezium’s Change Data Capture streams those changes to Kafka. The API returns success as soon as the database write commits—no waiting for Kafka. The CDC pipeline handles eventual delivery with exactly-once semantics.

Loading more...

Keyboard Shortcuts

Navigation
Next / previous item
j/k
Open post
oorEnter
Preview post
v
Post Actions
Love post
a
Like post
l
Dislike post
d
Undo reaction
u
Save / unsave
s
Recommendations
Add interest / feed
Enter
Not interested
x
Go to
Home
gh
Interests
gi
Feeds
gf
Likes
gl
History
gy
Changelog
gc
Settings
gs
Browse
gb
Search
/
General
Show this help
?
Submit feedback
!
Close modal / unfocus
Esc

Press ? anytime to show this help