Transactional Outbox Pattern
Transactional Outbox: start from the problem
See the responsibilities
An order is saved, then the application publishes an event. A crash between those steps leaves a saved order with no event. Publishing first creates the opposite risk: consumers hear about an order that did not commit.
This is a data and messaging pattern lesson. Follow the responsibilities, inspect the small JavaScript example and compare the design choices. The example is local teaching code; its boundary is explained before you apply the idea to a real service.
What you’ll learn
- Explain the problem Transactional Outbox addresses and the roles involved.
- Follow the example and distinguish this pattern from nearby alternatives.
- Choose when the benefit justifies the extra responsibilities and testing.
How the responsibilities work
Transactional Outbox records the business change and publication intent in the same supported local transaction. A relay later publishes committed outbox entries. That removes the need for a single atomic transaction across the database and broker, while adding durable relay work.
The code models one state replacement containing both records. It is not a real transactional database. A database implementation must use its supported atomic boundary, and the relay must account for publication followed by a lost acknowledgement.
Follow a small example
Read the JavaScript from top to bottom and compare the expected output. The values are chosen to make this pattern’s responsibility visible. This example runs in one local execution context; it does not create remote services, durable storage or production access controls.
JavaScript · local teaching example
let state = {orders: [], outbox: []};
function commitOrder(id) {
const event = {id: 'event-' + id, orderId: id};
state = {orders: [...state.orders, id], outbox: [...state.outbox, event]};
}
commitOrder('O1');
console.log(state.orders[0] + ' | ' + state.outbox[0].id);Expected output
O1 | event-O1Choose a design and inspect the consequence
Compare the design choices
Compare the visible consequences. With JavaScript enabled, choose an option in each scenario to assemble your decision record.
This is an exploratory design exercise. It does not run infrastructure, measure capacity or replace the lesson’s quiz.
Apply the pattern to two situations. Select one option in each situation to build a decision record. Every option and its consequence stays visible, and you can change your choices.
When it helps—and what it costs
Use it when a local data change must reliably lead to later messaging and both local records can share an appropriate transaction.
Relays can publish duplicates if they crash before recording success. Define event identity, consumer idempotency, ordering scope and retention. Multiple database owners cannot be made atomic simply by putting one outbox in one of them.
Distinguish the neighbouring patterns
Outbox records publication intent. Event Sourcing makes events authoritative history. Saga coordinates separate local business transactions; it can use outboxes within its participants.
Follow the existing outbox explanation, authoritative event history, separate-owner recovery to compare the specific responsibility before combining patterns.
For further reading, use AWS’s Transactional Outbox guidance. Compare it with this original example and the guarantee required by your application.
Check your pattern reasoning
Check your pattern reasoning
Compare the pattern boundaries
Name the responsibility before choosing the pattern
For Transactional Outbox, describe the problem, the participating roles and the observable contract. Use the example as a starting point and test the boundaries identified in the trade-offs. A pattern name alone does not establish correctness or performance.
Return to the design-pattern guide to compare families, or apply the idea alongside separate-owner recovery.
Reaching the recap and completing the quizzes are separate milestones.