⏱ 19 min read
There’s a particular kind of architectural mistake that looks sophisticated in a slide deck and painful in production: treating database change data capture as if it were event-driven architecture.
It’s an easy trap. You turn on CDC. Debezium reads the transaction log. Kafka topics begin to fill with inserts, updates, and deletes. Suddenly people start saying, “We’re event-driven now.” The room feels modern. The architecture review gets a green tick. A few quarters later, teams are reverse-engineering business meaning from column diffs, downstream services are coupled to table layouts they were never supposed to know about, and every schema change becomes a political negotiation.
That’s the tell.
A database row changed. Fine. Something happened in storage. But a changed row is not the same thing as a business event. “CustomerMovedHouse” is a business fact. “address_line_1 updated from X to Y” is a storage mutation. Those two things may be related, but they are not interchangeable. Confusing them is like mistaking tire tracks for the journey.
This distinction matters because architecture is mostly about preserving meaning while systems evolve. Domain-driven design taught us to care about the language of the business, the boundaries of models, and the contracts between teams. CDC operates below that level. It tells you what changed in persistence. Event-driven architecture, done properly, tells you what happened in the business.
And yes, both often use Kafka. Both may involve streams, microservices, materialized views, replay, consumers, offsets, and operational pipelines. The plumbing can look similar. The semantics are not. event-driven architecture patterns
That’s the whole argument in one sentence: CDC is a data integration mechanism; event-driven architecture is a behavioral integration style.
If you remember nothing else, remember that.
Context
Most enterprises didn’t start with clean bounded contexts and carefully curated domain events. They started with big transactional systems, shared databases, nightly ETL jobs, and years of accumulated logic buried in applications nobody wants to touch. Then came microservices, cloud migration, real-time analytics, API ecosystems, and pressure to make legacy systems participate in modern digital flows. cloud architecture guide
CDC emerged as the practical bridge.
It’s a good bridge. In many organizations, it is the only sane way to get timely data out of systems of record without hammering operational databases with polling queries. Reading the transaction log is less invasive than rewriting a core banking platform. It can feed Kafka. It can populate search indexes. It can refresh data lakes. It can synchronize read models. It can support progressive modernization.
That’s real value.
But because CDC is so useful, people over-assign it meaning. They begin to call every row mutation an event. They publish low-level table changes onto enterprise topics and ask downstream teams to infer intent. They build “event-driven microservices” whose behavior is actually driven by CRUD replication. This works right up until the business asks a simple question—“What exactly happened?”—and the answer is a pile of before/after images from twelve tables. microservices architecture diagrams
The issue is not technology. It is semantic elevation.
Domain-driven design gives us the lens here. In a healthy model, events emerge from the domain language of a bounded context. They express facts the business cares about: PolicyIssued, PaymentCollected, ShipmentDispatched, ClaimApproved. They are stable because the business concept is stable, even when internal persistence structures change. CDC, by contrast, emits facts about data representation. It reflects how a model is stored, not what the model means.
That’s why a mature architecture often uses both. CDC for extraction, migration, and integration at the data boundary. Domain events for decoupled collaboration between business capabilities.
They complement each other. They do not replace each other.
Problem
When teams conflate CDC with event-driven architecture, several problems show up fast.
First, consumers become coupled to physical data models. A downstream service subscribes to customer_table changes and now knows column names, update patterns, null semantics, denormalization quirks, even soft-delete conventions. The producer cannot refactor its schema without coordinating with every consumer. That is not loose coupling. That is distributed shared-database design wearing a Kafka badge.
Second, intent gets lost. Consider an order service. A single business action like “OrderPlaced” might update the order header, reserve inventory, create a payment authorization record, and insert an audit row. CDC sees four table mutations, maybe more. Which one should a downstream shipping service consume? In what order? Are they all part of one business fact, or separate technical consequences? The database log cannot answer that in business terms.
Third, transaction boundaries leak everywhere. CDC reflects commit order in storage, not necessarily the conceptual order of domain actions across bounded contexts. Downstream services start depending on incidental sequencing. If one table update arrives before another, consumers may make wrong assumptions or need ugly buffering logic.
Fourth, deletion and correction become toxic. Domain events are usually append-only business facts. CDC emits mutable state transitions. If a row is updated five times in ten seconds, consumers may see all five changes. Did the business care about all five? Often not. Sometimes one was an intermediate correction generated by a UI save flow. Downstream systems now inherit operational noise.
Fifth, ownership gets muddled. In domain-driven design, a bounded context owns its model and publishes an anti-corruption-friendly contract to others. With CDC, the publishing contract is often accidental: whatever the current schema happens to be. That’s not a product. That’s leakage.
The result is a familiar enterprise smell: lots of streaming infrastructure, very little semantic clarity.
Forces
This is not a morality play. Teams choose CDC-first patterns for good reasons.
They need non-invasive access to legacy data.
They cannot modify a packaged application to emit proper business events.
They want to reduce batch latency from hours to seconds.
They need Kafka pipelines for analytics, fraud detection, search indexing, or customer 360 views.
They are migrating from monolith to microservices and need a strangler path that doesn’t begin with a rewrite.
All reasonable.
At the same time, they face opposite pressures:
- They need stable contracts across teams.
- They want autonomy for service owners.
- They need auditability in business language, not just storage language.
- They must survive schema evolution.
- They cannot afford hidden coupling across dozens of consuming systems.
- They need regulatory reconciliation in some domains.
- They must keep operational risk low during migration.
So the real architectural question is not “CDC or events?” It is: where should storage-level truth stop, and where should business-level truth begin?
That boundary is an architecture decision.
Solution
Use CDC for what it is good at: extracting committed data changes from a persistence boundary. Use domain events for what they are good at: communicating meaningful business facts from a bounded context.
Do not ask one to impersonate the other.
In a clean design, a service that owns business behavior emits domain events explicitly from the application or domain layer, usually as part of a transactional outbox pattern to avoid dual writes. Those events are shaped around business semantics. They are versioned as contracts. They are published because another capability may need to react, not because a table happened to change.
CDC remains useful in three broad cases.
1. Legacy extraction.
For systems you cannot change, CDC is often the best mechanism to stream changes into Kafka and build downstream projections, caches, analytical feeds, or migration pipelines.
2. Migration support.
During strangler modernization, CDC can mirror state from a monolith into new services, helping teams carve out read models and bootstrap new bounded contexts.
3. Reconciliation and observability.
CDC can provide an independent record of state mutations for comparison, audit support, and backfill.
But the published interface between business capabilities should still move toward domain events, not table changes.
A useful rule is this:
- If consumers need to understand business meaning, publish domain events.
- If consumers only need state synchronization, CDC may be enough.
That distinction saves a lot of pain.
Architecture
A practical enterprise architecture often has both streams side by side: one semantic, one structural.
Pattern 1: Domain events from the owning service
The service handles commands, changes its own state, writes an outbox record in the same transaction, and a relay publishes domain events to Kafka. Consumers subscribe to contracts like OrderPlaced or PaymentCaptured.
This is event-driven architecture in the proper sense. The event is intentional. It expresses a domain fact. The database remains an implementation detail of the bounded context.
Pattern 2: CDC from a source database
Now compare that with database log capture.
This is a data pipeline. Valuable, often essential, but fundamentally different. Consumers are reacting to data changes, not business events. If they need business meaning, they must derive it, and derivation is where accidental complexity breeds.
Pattern 3: Combined migration architecture
The enterprise reality is usually a hybrid.
This is the pattern I recommend most often in large enterprises. CDC helps you extract from the old world. Domain events define the contract for the new one. Reconciliation sits between them while trust is earned.
That last part matters. Architects love clean target states. Operations teams live in the messy middle.
Domain semantics: the line that matters
The deepest issue here is semantics.
A domain event says, “A claim was approved.” That is a fact in the ubiquitous language. It can be understood by claims, fraud, customer communication, and finance, even if each bounded context interprets its consequences differently.
A CDC message says, “row in claim_status changed from PENDING to APPROVED.” That sounds close, but the similarity is dangerous. Maybe the status changed because of an automated rule, a manual override, a replay correction, or a migration script. Maybe multiple table updates together define approval. Maybe approval is not final until another aggregate changes. The row itself doesn’t tell you which.
Domain-driven design insists that business meaning belongs inside the bounded context. If you push raw persistence changes outside the boundary and ask other systems to infer intent, you have exported your ambiguity. Worse, you have forced other teams to model your internals.
That is why explicit events are healthier. They are acts of publication. Someone chose the name, the payload, and the contract because they knew what the business fact was.
CDC has no such intent. It is a mirror. Mirrors are useful. They do not explain.
Migration Strategy
This is where CDC shines, provided you don’t confuse a bridge with the destination.
A progressive strangler migration from monolith to microservices typically needs four stages.
1. Expose data without destabilizing the source
Begin by streaming CDC from the monolith database into Kafka. Use it to create read models, search indexes, and downstream views. This reduces dependence on direct database queries and gives new teams access to timely data.
At this stage, keep expectations modest. Treat CDC topics as internal integration assets, not enterprise business contracts.
2. Carve out a bounded context
Pick a domain seam with real business cohesion—customer preferences, product catalog, pricing rules, claims correspondence, something with a clear language and ownership. Build a new service around that capability. Let it own its own data. Introduce APIs and explicit domain events.
Now the new service speaks in business terms. It can still consume CDC from the monolith if needed, but its external contract should not simply echo old tables.
3. Introduce reconciliation
During migration, both old and new worlds may produce overlapping truths. You need reconciliation logic: compare aggregates, detect drift, reprocess missing messages, and maintain confidence dashboards.
This is not glamorous work, but it is what separates an enterprise migration from a conference talk. Eventually consistent systems without reconciliation are just hope in distributed form.
Useful reconciliation practices include:
- periodic state comparison between source and target
- replayable topics with idempotent consumers
- dead-letter handling with operational triage
- compensating workflows for mismatch correction
- lineage tracking from source transaction to downstream projection
4. Shift source-of-truth ownership
As the new bounded context matures, route writes to it first. Publish domain events from it. Reduce dependence on monolith CDC for that capability. Eventually the old table changes become just another internal migration artifact and can be retired.
That is the strangler pattern done properly: not merely replacing code, but replacing semantics and ownership.
Enterprise Example
Consider a large insurer modernizing policy administration.
The legacy core platform runs on a commercial package with a heavily customized relational schema. It cannot be modified easily to emit events. Teams need near-real-time feeds into Kafka for underwriting analytics, broker notifications, customer portals, and downstream document generation. CDC is the obvious starting point.
So they enable CDC on policy, premium, and endorsement tables and publish changes into Kafka. Initially this is a success. Dashboards improve. Search becomes timely. A new customer portal gets fresher policy data.
Then trouble starts.
The documents service subscribes to updates on policy tables to infer when a new policy has been issued. Sometimes it generates documents too early because premium rows arrive before policy status finalization. The broker notification service uses a status change to detect cancellations, but some changes are administrative corrections, not true customer-facing cancellations. The underwriting analytics team must join six CDC topics to reconstruct a single business milestone. Every schema change in the package now triggers downstream testing across dozens of integrations.
The architecture is “real-time,” but semantically brittle.
The insurer changes course.
For capabilities still trapped in the package, CDC remains in place as an extraction and synchronization mechanism. But for newly carved-out services—customer communications, billing adjustments, and broker interactions—the teams publish explicit domain events via an outbox: PolicyIssued, EndorsementAccepted, PremiumPaymentMissed, CancellationConfirmed.
A policy event gateway is introduced to translate selected CDC patterns from the legacy package into coarse-grained business events where possible, but only after domain experts define the semantics and edge cases. Not every table change gets translated. That restraint matters.
At the same time, a reconciliation service compares legacy policy state with the new domain projections. When the portal shows a policy status that does not match the core package, the issue is detected and repaired before it becomes a regulatory complaint.
After eighteen months, the architecture has both forms of streaming:
- CDC for legacy extraction and migration support
- Domain events for cross-service business collaboration
The difference is dramatic. Teams stop consuming package tables directly. Schema changes become manageable. New services integrate around policy lifecycle concepts rather than storage artifacts. Kafka remains central, but the contracts improve.
That is the mature shape.
Operational Considerations
Architects often underestimate this part because diagrams are calm and production is not.
CDC pipelines have specific operational characteristics:
Ordering.
Ordering is usually only meaningful within a partition and often scoped to a table key. Cross-table ordering is weak. If business meaning depends on composite sequencing, consumers need buffering, enrichment, or a higher-level publisher.
Duplicates.
You will see retries and replays. Consumers must be idempotent. This is not optional.
Schema evolution.
CDC topics are highly sensitive to table changes. Adding nullable columns is one thing. Splitting a table, changing key semantics, or refactoring normalization can break downstream assumptions badly.
Backfills and snapshots.
Initial loads and replay behavior need careful control. A snapshot can look like a flood of historical changes. Some consumers should process it; others should not.
Delete semantics.
Hard deletes, soft deletes, tombstones, and retention policies all need explicit treatment. Too many teams discover this only after a GDPR or retention requirement lands.
Lag and reprocessing.
A “real-time” stream with ten minutes of lag during peak load is not real-time in any business sense. Monitor end-to-end latency, not just broker health.
For domain events, the operational profile is different:
Contract governance.
Events are products. They need versioning strategy, ownership, documentation, and lifecycle management.
Transactional integrity.
Use the outbox pattern or equivalent. Do not trust best-effort dual writes between database and Kafka.
Semantic monitoring.
Measure business flow completeness. For example: every OrderPlaced should eventually correlate to either OrderShipped or OrderCancelled. Infrastructure metrics alone do not tell you that.
A good enterprise platform supports both patterns but does not blur them. Separate topic naming, metadata, documentation, and governance help. Teams should know whether they are consuming a structural change feed or a business event contract. EA governance checklist
Tradeoffs
Let’s be blunt: CDC is often faster to adopt than proper domain events.
That is its superpower and its temptation.
CDC advantages
- non-invasive for existing systems
- ideal for legacy modernization
- efficient extraction for analytics and synchronization
- useful for search, caches, and data lakes
- often easier politically than changing core application code
CDC costs
- weak business semantics
- tight coupling to schema and storage design
- noisy event streams from low-level mutations
- hard consumer logic when meaning spans multiple tables
- brittle downstream dependencies over time
Domain event advantages
- strong business meaning
- better fit for bounded contexts and team autonomy
- more stable contracts across schema changes
- cleaner choreography between microservices
- easier reasoning about business workflows
Domain event costs
- requires application changes
- demands domain modeling discipline
- needs outbox/infrastructure support
- may be impossible in vendor packages or legacy cores
- takes longer to define well
This is why architecture is a tradeoff exercise, not a slogan contest. If you have a mainframe-backed ledger that cannot be changed, insisting on pure event-driven design from day one is fantasy. If you are building a new customer onboarding platform and choose to publish table mutations instead of business events, that is negligence.
Failure Modes
Several failure modes repeat across enterprises.
1. Topic-per-table as enterprise contract
This is the classic trap. Every table gets a Kafka topic, and downstream systems consume them directly. The organization has recreated shared database coupling with more infrastructure.
2. Reverse-engineered business meaning
Consumers infer OrderCompleted from a sequence of row changes. It works until an internal workflow changes and downstream logic quietly becomes wrong.
3. CDC as command bus
Teams start triggering business actions from low-level data changes. One service updates a flag; another interprets that as an instruction. Congratulations, you now have hidden control flow mediated by persistence side effects.
4. Missing reconciliation
During migration, discrepancies accumulate between source systems and derived views. No one notices until customers do.
5. Over-trusting exactly-once stories
Brokers, connectors, and frameworks all have nuanced guarantees. Design for duplicates, gaps, replay, and poison messages.
6. Ignoring bounded contexts
A single “enterprise customer topic” built from a master table tries to satisfy marketing, billing, risk, and service operations. It satisfies none of them well because each context means something different by customer.
The cure in almost every case is the same: restore semantic ownership.
When Not To Use
Do not use CDC as the primary integration pattern when:
- you are designing a new bounded context from scratch
- consumers need business intent, not state diffs
- schema autonomy is important
- workflows depend on explicit domain milestones
- you need stable external contracts over time
- domain invariants matter more than raw state propagation
And do not use event-driven architecture as an ideological replacement for all data integration. If what you need is near-real-time replication into an analytics platform, CDC may be simpler and more honest.
Likewise, if the source system is a vendor package that cannot emit events, pretending otherwise wastes time. Use CDC, but name it correctly and contain the coupling.
The pattern should fit the job. Architecture gets expensive when tools are asked to be something they are not.
Related Patterns
A few related patterns matter here.
Transactional Outbox
The standard way to publish domain events reliably without dual-write risk. The application writes business state and an outbox record in one local transaction; a relay then publishes to Kafka.
Event Sourcing
Not the same as CDC. Event sourcing stores domain events as the system of record. CDC reads mutations from a database log. Superficially similar, fundamentally different.
CQRS
CDC can help build read models, especially during migration. Domain events can also feed read models. The choice depends on whether the read model needs semantic events or raw state changes.
Anti-Corruption Layer
Vital when translating legacy CDC into a cleaner domain model. Don’t let old table structures leak straight into new services.
Strangler Fig Pattern
The right migration frame for replacing legacy capabilities incrementally while keeping the system alive.
Data Mesh / Streaming Data Platform
Useful organizationally, but still not a reason to confuse analytical change streams with operational domain contracts.
Summary
CDC is not event-driven architecture.
It is a powerful, pragmatic, often indispensable way to capture and distribute data changes. It is excellent for legacy extraction, migration, synchronization, and building downstream projections. In a Kafka-based enterprise, it often becomes the first real-time artery out of old systems. That is worth celebrating.
But a database mutation is not a business event. CDC reflects storage. Domain events express meaning. One tells you that data changed. The other tells you what happened in the business. The difference is not semantic nitpicking; it is the boundary between manageable evolution and long-term coupling.
The best enterprise architectures use both, deliberately.
Use CDC to get facts out of systems that cannot yet speak well for themselves. Use domain events when a bounded context is mature enough to publish its own language. During migration, let CDC support the strangler path, but let explicit business events define the destination. Add reconciliation, because distributed truth always needs checking. Use Kafka if it fits, but remember that a broker does not confer semantics.
In short: CDC is a mirror. Event-driven architecture is a conversation.
Good systems need both. Great architects know which is which.
Frequently Asked Questions
What is enterprise architecture?
Enterprise architecture aligns strategy, business processes, applications, and technology in a coherent model. It enables impact analysis, portfolio rationalisation, governance, and transformation planning across the organisation.
How does ArchiMate support architecture practice?
ArchiMate provides a standard language connecting strategy, business operations, applications, and technology. It enables traceability from strategic goals through capabilities and services to infrastructure — making architecture decisions explicit and reviewable.
What tools support enterprise architecture modeling?
The main tools are Sparx Enterprise Architect (ArchiMate, UML, BPMN, SysML), Archi (free, ArchiMate-only), and BiZZdesign. Sparx EA is the most feature-rich, supporting concurrent repositories, automation, scripting, and Jira integration.