Skip to main content
Articles/outbox-pattern-deep-dive-event-driven-architectures/outbox-vs-cdc-a-decision-matrix-for-event-driven-microservices
Scenario Article

Outbox vs CDC: A Decision Matrix for Event‑Driven Microservices

Problem: “When does my data get out of sync with my events?”

Abstract Algorithms•Sep 4, 2026
💡 Executive Summary

📦 🛠️ Choose the right event source: Outbox is simple but can lag, Transactional Outbox guarantees atomicity, CDC is real‑time but complex.

🎯 System Scenario & Requirements

System Goal

📦 🛠️ Choose the right event source: Outbox is simple but can lag, Transactional Outbox guarantees atomicity, CDC is real‑time but complex.

Actors & Components

  • Database
  • Event Worker / CDC Agent
  • Message Broker (Kafka)
  • Order Service
  • Outbox Table
  • CDC Agent
  • Outbox Worker
  • Kafka Topic

Functional Requirements

  • TLDR: 📦 🛠️ Choose the right event source: Outbox is simple but can lag, Transactional Outbox guarantees atomicity, CDC is real‑time but complex.
  • In a microservice, you often write to a database and want to publish an event to a message broker.
  • If the write succeeds but the publish fails (network glitch, broker down), you end up with a data‑event split : the database reflects the change, but downstream services never see it.

Constraints & Assumptions

  • Constraints are derived from the rebuilt scenario stages.

⚡ Execution Stages & Decision Trace

1

Stage 1 — The Three Strategies

Action / Decision:

Apply event-driven-architecture to The Three Strategies.

Component Behavior:

The system executes the mechanisms described for “The Three Strategies”.

Observable Output: Callers and operators can verify the stage outcome for “The Three Strategies”.
Read Full Stage in Deep Dive ↓
📖

Deep Dive

Detailed Architectural Analysis

Scenario context

📦 🛠️ Choose the right event source: Outbox is simple but can lag, Transactional Outbox guarantees atomicity, CDC is real‑time but complex. In a microservice, you often write to a database and want to publish an event to a message broker. If the write succeeds but the publish fails (network glitch, broker down), you end up with a data‑event split: the database reflects the change, but downstream services never…

This walkthrough focuses on Problem: “When does my data get out of sync with my events?”.

Before making a design choice, clarify:

  • In a microservice, you often write to a database and want to publish an event to a message broker.
  • If the write succeeds but the publish fails (network glitch, broker down), you end up with a data‑event split : the database reflects the change, but downstream services never see it.
  • This inconsistency can cascade, leading to stale caches, incorrect analytics, or failed business flows.

Concepts in play: event-driven-architecture, transaction-isolation-distributed-acid, message-queues, consistency-replication

Stage 1 — The Three Strategies

Observable outcome: You can explain how “The Three Strategies” changes the architecture or implementation path.

Stage 2 — Decision Matrix: When to Pick Each

Observable outcome: You can explain how “Decision Matrix: When to Pick Each” changes the architecture or implementation path.

Stage 3 — Quick Implementation Sketch (Transactional Outbox with Spring Data JPA)

  • A lightweight worker polls outbox events and publishes to Kafka, then marks the row as sent or deletes it.
  • The key is that the DB transaction guarantees the order write and the outbox row are committed together.
@Entity
@Table(name = "orders")
class Order {
  @Id @GeneratedValue Long id;
  String status;
  // …
}

@Entity
@Table(name = "outbox_events")
class OutboxEvent {
  @Id @GeneratedValue Long id;
  String aggregateType;
  Long aggregateId;
  String payload; // JSON
  Instant createdAt;
}

@Service
class OrderService {
  @Transactional
  void placeOrder(Order order) {
    orderRepository.save(order); // domain write

    // outbox write in same tx
    OutboxEvent e = new OutboxEvent();
    e.setAggregateType("Order");
    e.setAggregateId(order.getId());
    e.setPayload("{\"orderId\":" + order.getId() + "}");
    e.setCreatedAt(Instant.now());
    outboxRepository.save(e);
  }
}

Observable outcome: The key is that the DB transaction guarantees the order write and the outbox row are committed together.

Stage 4 — Architecture Kit Diagram

  • { "version": 1, "title": "Event Source Strategies", "description": "Comparing Outbox, Transactional Outbox, and CDC flows", "view": "data-flow", "pattern": "reactive-pull", "direction": "LR", "groups": [ { "id": "db", "…

Observable outcome: { "version": 1, "title": "Event Source Strategies", "description": "Comparing Outbox, Transactional Outbox, and CDC flows", "view": "data-flow", "pattern": "reactive-pull", "direction": "LR", "groups": [ { "id": "db", "…

End-to-end outcome

After working through the stages, you should be able to explain:

    • Transactional Outbox is the gold‑standard for strong consistency.
    • Outbox is great for simpler, low‑latency needs.

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

    • Transactional Outbox is the gold‑standard for strong consistency.
    • Outbox is great for simpler, low‑latency needs.
  • Apply event-driven-architecture to The Three Strategies.
  • Apply transaction-isolation-distributed-acid to Decision Matrix: When to Pick Each.
  • A lightweight worker polls outbox events and publishes to Kafka, then marks the row as sent or deletes it.

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit