What is the Outbox Pattern?
The Outbox Pattern guarantees consistency between database operations and event publishing in distributed systems. The core idea: record domain events in a special table (the outbox), and let an external component — the Outbox Publisher — process and publish those events to other systems.
Why is the Outbox Pattern necessary?
Imagine an order that can be confirmed or canceled. When that happens, you usually need to update the database and notify other systems — inventory, email, finance. The classic problem: how do you guarantee that if the database was updated, the event will also be published? What if publishing fails after the order is already confirmed?
How does the Outbox Pattern work in practice?
Instead of updating the database and publishing the event at the same time (risking one failing), do both in a single transaction: save the order and record the event in the outbox table. That table acts as a local queue of events to process. Nothing is sent externally yet.
Practical usage example
When confirming an order:
- Update the order status to "CONFIRMADO" in the database.
- Create a record in the outbox table: "Order X was confirmed."
All of this happens in the same transaction. On error, nothing is saved. On success, both the order and the event are recorded atomically.
Example of a record in the outbox table
{
"id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"aggregate_id": "pedido-123",
"aggregate_type": "pedido",
"type": "PedidoConfirmado",
"payload": {
"pedido_id": "pedido-123",
"cliente_id": "cliente-456",
"data_confirmacao": "2024-06-10T10:00:00Z"
},
"status": "pending",
"created_at": "2024-06-10T09:59:59Z"
}
The payload holds event-specific data — order ID, customer ID, confirmation date.
The role of the Outbox Publisher
The Outbox Publisher is a separate process that monitors the outbox table. Periodically it fetches pending events, publishes each one to a message broker (Kafka, RabbitMQ, SQS, etc.), and only then marks the event as processed. If publishing fails, the event stays in the table and is retried later — nothing is lost.
Example of a processed event
After successful publication:
{
"id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"aggregate_id": "pedido-123",
"aggregate_type": "pedido",
"type": "PedidoConfirmado",
"payload": {
"pedido_id": "pedido-123",
"cliente_id": "cliente-456",
"data_confirmacao": "2024-06-10T10:00:00Z"
},
"status": "processed",
"created_at": "2024-06-10T09:59:59Z",
"processed_at": "2024-06-10T10:00:01Z"
}
status is now "processed", and processed_at records when the event was published.
If publishing fails, the event can be retried a limited number of times before moving to a dead-letter queue for later analysis.
Advantages of the Outbox Pattern
- Guaranteed consistency: The order is only confirmed if the event is also recorded — no split between database and external systems.
- Resilience: If the broker is down, events stay in the outbox and are sent as soon as possible.
- Decoupling: The order service only records what happened; the Outbox Publisher handles delivery.
- Observability: You can monitor, reprocess, or audit pending or failed events.
Conclusion
The Outbox Pattern is a reliable way to keep the database and the outside world in sync, even under failures — useful whenever you need consistency and traceability in distributed systems.

Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.