Prasoon AI
ServicesInsights
Let’s talk
GOOD IDEAS DESERVE GREAT ENGINEERING.Explore the possibilities.

WHAT I BUILD

AI systems

Agents, private knowledge, and production AI.

SaaS platforms

Scalable software, from first release to growth.

HOW WE WORK

Services

Engineering expertise for your next challenge.

Industries

Solutions grounded in your business context.

IDEAS & PERSPECTIVES

Insights

Practical thinking on AI and architecture.

About my approach

Bridging research and production.

Available for select projectsDiscuss your project
All insights/Event Architecture

Event Architecture / Practical engineering

Transactional Outboxes: Ordering, Replay, and Consumer Correctness

Close the database-to-broker failure gap without pretending delivery is exactly once. Design aggregate sequences, consumer checkpoints, and deliberate replay behavior.

P.
Prasoon ThakurAI systems architect
September 20, 20265 min read
THE ENGINEERING SERIESEvent Architecture
CommitOrder + event
RelayPublish intent
ApplyCheckpoint
Aggregate / order_204 v17 → v18 → v19
Event ArchitectureIdeas, connected to implementation.
In this article6 sectionsContents +
  1. 01Start with the dual-write failure
  2. 02Define the aggregate before defining the topic
  3. 03Choose a relay with an explicit recovery model
  4. 04Make consumer state advancement atomic
  5. 05Replay needs a separate operating mode
  6. 06Operate the oldest unhandled event

Start with the dual-write failure

An order update commits successfully, but publishing OrderConfirmed fails. The warehouse never sees the order. Reverse the sequence and a different problem appears: the message is published, then the database transaction rolls back. The warehouse sees an order that does not exist.

The transactional outbox pattern places the business mutation and the event record in the same database transaction. A separate relay publishes committed records. Its guarantee is atomic publication intent, not atomic delivery to every downstream system. The pattern's original description explicitly calls out duplicate publication and the need for idempotent consumers. Transactional outbox pattern

The design below extends that boundary with explicit sequencing and replay rules. Those rules are application design recommendations; the broker cannot infer them from a JSON payload.

Define the aggregate before defining the topic

An aggregate is the smallest business object whose transitions must remain ordered: an order, subscription, or document version. Give each aggregate a monotonically increasing version allocated within the transaction that updates it. A timestamp is inadequate when two changes occur together or clocks disagree.

An illustrative event envelope is:

System Source
{
  "event_id": "evt_7c45",
  "aggregate_type": "order",
  "aggregate_id": "ord_204",
  "aggregate_version": 18,
  "schema_version": 2,
  "event_type": "order.confirmed",
  "correlation_id": "checkout_91",
  "payload": { "order_id": "ord_204" }
}

The aggregate version expresses business order. The schema version describes payload interpretation. The correlation ID connects a wider workflow. None replaces the event ID used to identify a particular publication.

Put a unique constraint on aggregate identity plus aggregate version. Update the aggregate with a lock or optimistic version predicate, and insert the event before committing. Never calculate the next version with an unlocked MAX(version) + 1 query.

Choose a relay with an explicit recovery model

RelayOperational advantageFailure boundary to address
Database pollerSimple deployment and inspectionPublish succeeds but marking sent fails
Change data captureReads committed database changesConnector offsets, log retention, and bootstrap
Application job after commitLow initial complexityProcess dies before durable job publication

A poller can claim work in short transactions, publish outside the transaction, and finalize with a lease token. If the lease expires, a replacement worker may publish again. Consumers must tolerate that. For strict aggregate ordering, prevent later versions from overtaking earlier unpublished versions or make the consumer buffer gaps.

Debezium's outbox router uses the aggregate ID as the message key by default, which supports order within Kafka partitions. That is a partitioning mechanism, not a global ordering guarantee or a substitute for correct producer sequencing. Debezium outbox event router

Make consumer state advancement atomic

A consumer should commit its projection update, processed-event marker, and aggregate checkpoint together. If those records live in separate databases, the same dual-write problem reappears on the receiving side.

For a sequential projection, compare incoming version v with checkpoint c. If v is the next expected version, apply it. If it is an already recorded event, acknowledge without repeating the effect. If it creates a gap, park it and request recovery. A lower version with an unknown event ID deserves investigation; blindly discarding it can hide corruption or an invalid replay.

Some projections can replace their entire state from a newer authoritative snapshot. Others, such as accounting deltas, must process every transition. Document which kind each consumer implements. “Last event wins” is a design choice with consequences, not a universal solution.

Replay needs a separate operating mode

A replay that rebuilds a search index should not resend customer emails. Classify consumers as projection builders or external-effect executors. Give a replay a run ID, selected event range, target projection version, and explicit policy for external effects.

Build a replacement projection beside the live one. Compare record counts, domain totals, missing identifiers, and representative queries before switching readers. A replay is not proven correct because the queue drained.

Keep event schemas compatible with retained history. Additive fields need defaults; changed meanings may need an upcaster or a new event type. Test the oldest retained schema against the newest consumer before deployment.

Operate the oldest unhandled event

Monitor the age of the oldest unpublished event, sequence gaps, duplicate rate, consumer lag, and poison-event age. Alert on business delay thresholds rather than one universal queue-length limit. Ten stalled orders may matter more than a million optional analytics events.

Inject a relay crash immediately after broker acknowledgment. Run two consumers against the same event. Replay an old schema into a new projection. Introduce a missing aggregate version and verify that later state cannot silently advance past it.

Use an outbox when a committed change must reliably produce asynchronous work. Avoid adding one to every database write by habit. Its value comes from an explicit delivery obligation and a recovery path that operators can understand.

Frequently asked questions

Does an outbox guarantee exactly-once delivery?

No. It atomically records a business change and its publication intent. Relays can deliver duplicates, so consumers still need durable deduplication or idempotent state updates.

Should every event have a global sequence?

Only when the business genuinely requires global ordering. Per-aggregate sequences usually express the required consistency with less contention.

About the author

Prasoon Thakur

Prasoon is an AI systems architect focused on reliable agents, retrieval, LLM operations, and scalable SaaS platforms. His work connects model behavior to the controls production teams need: evaluation, observability, security, and cost discipline.

GitHubUpwork profile

Need a reliable production system?

Turn the patterns in this guide into a scoped system design, delivery plan, and measurable reliability target.

Start a strategy session

Related insights

API Engineering

Cursor Pagination Under Concurrent Writes: A Consistency Contract

4 min read
Data Lifecycle

Distributed Data Deletion: Tombstones, Derived Copies, and Restore Safety

5 min read

Active Now • 24/7 Availability

Engaging with teams
from Silicon Valley to Singapore.

I operate as a high-availability resource. To maintain secure collaboration, all global engagements are managed via Upwork.

Discuss your project Direct collaboration through Upwork.

Global / Remote

24/7 Timezone Agnostic

Syncing with USA, Europe, UAE & Singapore

Secure Engagement

Top Rated Expert on Upwork

Prasoon AI

Thoughtful architecture.
Software built for the real world.

Based online. Working worldwide.

Explore

AI systemsSaaS platformsServicesIndustries

Discover

Engineering insightsMy approach

Connect

Upwork GitHub Open to project inquiries

© 2026 Prasoon Thakur

Independent thinking. Dependable engineering.Back to top ↑