Back to insights

Integration Architecture & Change Data Capture

Change Data Capture is not a business event contract

JSBC Labs8 min read

A record changed is not why it changed

A downstream system subscribes to OpportunityChangeEvent and treats ClosedWon as an instruction to provision service. The design feels event-driven, but the payload reports a database mutation, not a commercial decision. A correction, data migration, merge, automation replay or reopened opportunity can produce a similar change with very different business meaning. The consumer has inferred intent from state.

Salesforce positions Change Data Capture (CDC) as a near-real-time way to receive record changes and synchronize external data stores. That is its architectural strength. The JSBC Labs position is that CDC should remain a change-notification contract unless the business has deliberately defined more. When consumers need meaning such as OrderApproved or CustomerOffboarded, publish a domain event whose name, eligibility and ownership express that decision.

Choose CDC for replication, not command delivery

CDC fits caches, search indexes, analytical stores and operational replicas that need to follow Salesforce creates, updates, deletes and undeletes. It avoids wasteful polling and can carry enough changed state for a consumer to update its own representation. Pub/Sub API provides a unified subscription interface with flow control so a client can request events at a pace it can process.

It is a weaker fit when one precise business transition must cause one controlled action. A record can be edited through the UI, API, Flow, Apex, imports and administrative maintenance. All can emit change events without promising that the change passed the semantic process expected by a consumer. Use CDC to observe state; use an explicit platform event or API operation when the publisher must declare intent and owns the contract.

Read the header as protocol data

The ChangeEventHeader is not decoration. changeType, recordIds, changedFields, commitTimestamp, transactionKey, sequenceNumber and related fields tell a subscriber how the mutation entered the stream. Salesforce documents that changes from one transaction share a transactionKey and use sequenceNumber to show their order within that transaction. Consumers that ignore these fields discard information needed to reconstruct a coherent change set.

Do not assume one message always represents one familiar record update. Salesforce can group records that receive the same change, and some conversions can produce wildcard record identifiers. Build a parser for the documented protocol rather than a sample payload. Log the channel, replay ID, schema ID, transaction key, sequence number and record identifiers so support teams can trace what was received and how it was applied.

Ordering is local evidence, not global truth

Salesforce stores change events in commit order, and subscribers receive them in replay-ID order. Within a transaction, transactionKey and sequenceNumber help preserve the relationship among changes. That does not create a universal business order across every channel, middleware queue and target system. Parallel consumers, retries and downstream processing can still complete out of order.

Make target writes conditional on a durable version or event position. Partition work by the identity that requires ordering, keep the partition small enough to scale and reject stale updates rather than blindly applying arrival order. If several object changes must become one downstream outcome, aggregate by transaction where appropriate or re-read authoritative state after the transaction. Do not pretend that faster delivery guarantees causal consistency.

Replay is a recovery window, not a backup

Salesforce stores platform events and CDC events for 72 hours. A Pub/Sub API client can persist the last processed replay ID and request retained events after reconnecting. Replay IDs are opaque and are not guaranteed to be contiguous, so a consumer must store the value supplied by Salesforce and never calculate the next one.

The retention window defines an operational deadline. If a consumer is unavailable beyond it, replay alone cannot repair the replica. Design a reconciliation path before production: bulk-query current state, compare durable identifiers and versions, repair discrepancies, then resume streaming from a controlled point. Alert on subscription lag in time and volume, not only on connection status. A connected consumer can still be too far behind to recover safely.

Assume duplicate processing

A subscriber can process an event, update the target and fail before saving its checkpoint. On restart it should replay the event, because losing an update is worse than seeing it again. That means the handler must be idempotent. The target operation should converge on the same state when the same change is applied more than once.

Persist a consumer checkpoint only after the business write and any required audit record are durable. Where those writes cannot share a transaction, use an inbox table keyed by channel and replay identity, or an application key derived from the mutation context. Separate transient retries from poison events: quarantine payloads that repeatedly fail validation, advance processing according to an explicit policy and keep a repair route that does not silently discard later changes.

Treat the event schema as moving

CDC subscriptions use the latest API version, and Salesforce states that messages reflect the latest field definitions of the source object. When the object schema changes, the event schema ID changes. A subscriber tied to one generated payload shape can therefore fail after an apparently unrelated field addition or type change.

Resolve and cache schemas by schema ID, support compatible additions and test metadata changes against representative consumers. Version the consumer’s internal mapping independently of the Salesforce object. Change events contain changed values, not necessarily every unchanged field required by a target; Salesforce provides event enrichment for selected fields such as external IDs. Use enrichment deliberately, or re-query state, rather than assuming null means a field was cleared.

Budget delivery and backpressure

Event-driven does not mean capacity-free. Salesforce documents shared delivery allocations for high-volume platform events and CDC events, while client processing time and event volume determine how quickly a subscriber reaches the tip of the stream. A broad all-change subscription can consume entitlement and force every downstream service to filter noise after delivery.

Select only the objects and fields that serve an owned integration outcome. Use custom channels and event filters where appropriate, size Pub/Sub API fetch requests to actual processing capacity and monitor throughput, lag, retry depth and allocation usage. Apply backpressure before downstream dependencies collapse. Scaling the reader without scaling target writes merely moves the queue to a less visible place.

Operate CDC as a data product

Give each subscription an owner, consumer catalogue, service objective, checkpoint store, replay procedure, schema-compatibility policy and reconciliation runbook. Test create, update, delete, undelete, merged records, grouped changes, multi-record transactions, duplicate delivery, schema change and an outage longer than the replay window. Validate outcomes against production-like volume and data skew.

Measure freshness and correctness separately. Low latency is irrelevant if mappings are wrong; perfect mappings are irrelevant if the replica is days behind. Reconcile record counts, key fields and deletion state on a schedule, then investigate drift to improve the stream handler. CDC is powerful because it exposes Salesforce mutations efficiently. It becomes reliable architecture only when consumers stop mistaking those mutations for complete business meaning.

Official references

Continue reading