Transactional Outbox vs. Change Data Capture: Choosing the Right Event Pipeline
Problem: âLost Eventsâ in Microservices
đŻ Pick transactional outbox for guaranteed atomicity and minimal latency, or CDC for a lightweight, logâdriven approach when you can tolerate small gaps.
đŻ System Scenario & Requirements
System Goal
đŻ Pick transactional outbox for guaranteed atomicity and minimal latency, or CDC for a lightweight, logâdriven approach when you can tolerate small gaps.
Actors & Components
- Domain Service
- Relational DB
- Outbox Worker
- CDC Consumer
- Message Broker
- Service
- DB
- Kafka
Functional Requirements
- TLDR: đŻ Pick transactional outbox for guaranteed atomicity and minimal latency, or CDC for a lightweight, logâdriven approach when you can tolerate small gaps.
- Youâve built a service that updates a user profile and must publish an UserUpdated event.
- If you write the event to the broker after the DB commit, a crash in between can leave the database updated but no event sentâbreaking downstream consumers.
Constraints & Assumptions
- Constraints are derived from the rebuilt scenario stages.
⥠Execution Stages & Decision Trace
Stage 1 â Solution: Transactional Outbox
With an outbox, the service writes both the domain record and a new row in an outbox table inside the same transaction.
A separate âoutbox workerâ polls this table, serializes the payload, and pushes it to Kafka (or another broker).
Deep Dive
Scenario context
đŻ Pick transactional outbox for guaranteed atomicity and minimal latency, or CDC for a lightweight, logâdriven approach when you can tolerate small gaps.
Youâve built a service that updates a user profile and must publish an UserUpdated event. If you write the event to the broker after the DB commit, a crash in between can leave the database updated but no event sentâbreaking downstream consumers. You need a pattâŚ
This walkthrough focuses on Problem: âLost Eventsâ in Microservices.
Before making a design choice, clarify:
- Youâve built a service that updates a user profile and must publish an UserUpdated event.
- If you write the event to the broker after the DB commit, a crash in between can leave the database updated but no event sentâbreaking downstream consumers.
- You need a pattern that keeps the DB write and event publication transactionally consistent .
Concepts in play: event-driven-architecture, transaction-isolation-distributed-acid, message-queues, consistency-replication
Stage 1 â Solution: Transactional Outbox
- With an outbox, the service writes both the domain record and a new row in an outbox table inside the same transaction.
- A separate âoutbox workerâ polls this table, serializes the payload, and pushes it to Kafka (or another broker).
- Because the DB commit guarantees both rows exist, the worker will never see a partially written state.
-- Inside a single transaction
INSERT INTO users (id, name) VALUES (:id, :name);
INSERT INTO outbox (id, aggregate_id, payload, created_at)
VALUES (uuid_generate_v4(), :id, jsonb_build_object('type','UserUpdated','id',:id), now());
# pseudoâworker
for event in outbox_table.fetch_unprocessed():
broker.publish(event.payload)
mark_as_sent(event.id)
Observable outcome: Because the DB commit guarantees both rows exist, the worker will never see a partially written state.
Stage 2 â Tradeâoffs
Observable outcome: You can explain how âTradeâoffsâ changes the architecture or implementation path.
Stage 3 â When to Use Which
- Transactional Outbox : When exactlyâonce delivery and strong consistency are nonânegotiable (e.g., financial transactions).
- CDC : When you can tolerate occasional retries or gaps, or you already have a logâdriven architecture in place (e.g., microâservices that read from a shared event store).
Observable outcome: - CDC : When you can tolerate occasional retries or gaps, or you already have a logâdriven architecture in place (e.g., microâservices that read from a shared event store).
Stage 4 â Architecture Kit Diagram
- { "version": 1, "title": "Transactional Outbox vs CDC Pipelines", "description": "Sideâbyâside view of the two event publishing patterns", "view": "data-flow", "pattern": "reactive-pull", "direction": "LR", "groups": [âŚ
Observable outcome: { "version": 1, "title": "Transactional Outbox vs CDC Pipelines", "description": "Sideâbyâside view of the two event publishing patterns", "view": "data-flow", "pattern": "reactive-pull", "direction": "LR", "groups": [âŚ
Stage 5 â Bottom Line
- Choose the outbox when you canât afford a single lost event; lean on CDC when you need minimal latency and can handle eventual consistency.
- The right choice hinges on your serviceâs tolerance for gaps versus the cost of extra infrastructure.
Observable outcome: The right choice hinges on your serviceâs tolerance for gaps versus the cost of extra infrastructure.
End-to-end outcome
Youâve built a service that updates a user profile and must publish an UserUpdated event.
Alternatives considered
The source focuses on one primary design. A second architecture is not compared, so no alternative is invented here.
Failure and degraded modes
The source does not isolate a separate failure mode. Limits that matter for the scenario are called out in the stage decisions above.
Key learnings
- With an outbox, the service writes both the domain record and a new row in an outbox table inside the same transaction.
- Apply transaction-isolation-distributed-acid to Tradeâoffs.
- Transactional Outbox : When exactlyâonce delivery and strong consistency are nonânegotiable (e.g., financial transactions).
- { "version": 1, "title": "Transactional Outbox vs CDC Pipelines", "description": "Sideâbyâside view of the two event publishing patterns", "view": "data-flow", "pattern": "reactive-pull", "direction": "LR", "groups": [âŚ
