Event-Driven Architecture

Platform Events vs Change Data Capture in Salesforce

A docs-first comparison of Salesforce's two main event-bus patterns, with Apex code examples, replay guidance, channel naming, and a practical decision framework for architects.

14 min readPublished April 13, 2026By Shivam Gupta
Shivam Gupta
Shivam GuptaSalesforce Architect and founder at pulsagi.com
Salesforce event bus architecture

This article stays anchored to current Salesforce documentation for the event bus, Pub/Sub API, CDC setup, durability, and event channel conventions.

Introduction

Salesforce's modern event bus gives you two very different ways to tell the outside world that something happened: Platform Events and Change Data Capture (CDC). Both are durable for replay within the retention window, both can be consumed through Pub/Sub API, and both can drive Apex triggers, Flow, and external integrations. But they are not interchangeable.

The cleanest way to choose is to ask one question first: are you publishing a business fact, or are you exposing a record mutation?

Short version: use Platform Events when your producer wants to emit an intentional business signal such as Order_Submitted__e. Use CDC when subscribers need to know that Salesforce data itself changed, such as an Account, Opportunity, or supported custom object being created, updated, deleted, or undeleted.

Another important framing from Salesforce documentation: Pub/Sub API is not a third event type. It is a transport and subscription interface that works with both Platform Events and CDC.

What each event type means

Platform Events

  • You decide when the event is emitted.
  • You own the event contract and field design.
  • Best for business milestones, orchestration, and decoupled domain messaging.
  • Typical channel shape is /event/EventName__e.

Change Data Capture

  • Salesforce emits the event automatically when tracked records change.
  • The payload shape follows the underlying object plus ChangeEventHeader.
  • Best for sync, replication, audit-style consumers, and downstream data pipelines.
  • Typical channel shape is /data/AccountChangeEvent or /data/MyObject__ChangeEvent.

That difference in publisher intent is the whole architecture story. Platform Events express meaning at the business layer. CDC expresses meaning at the data-change layer.

Architectural inference from the docs: if an external system should react only after committed Salesforce work is real, a custom Platform Event configured with Publish After Commit is usually the safer contract than exposing raw object triggers or assuming every record update deserves an integration event.

Side-by-side comparison

AreaPlatform EventsChange Data Capture
Who emits the messageYour app, Flow, Apex, or an API client chooses when to publish.Salesforce publishes automatically when a tracked record changes.
What the payload meansA business contract such as order submitted, invoice approved, shipment delayed.A change notification for a Salesforce record operation.
Schema ownershipYou define custom event fields and keep the contract stable for subscribers.Salesforce defines the event shape from the source object and the change header.
Can you publish it manuallyYes. Apex, Flow, data APIs, and Pub/Sub API can publish supported platform events.No. You enable CDC for supported objects and Salesforce produces the events.
Best-fit consumerApps that care about business outcomes and process steps.Apps that care about record deltas, synchronization, or data movement.
Typical topic name/event/Order_Submitted__e/data/AccountChangeEvent
Retention and replayStored on the event bus for 72 hours and replayable within that window.Stored on the event bus for 72 hours and replayable within that window.
Good fitYou want a stable, intentional message that survives source-object refactors.You want downstream systems to track what changed on Salesforce records.
Poor fitYou are trying to mirror every row-level mutation for replication.You need a curated business event rather than every qualifying CRUD mutation.

A lot of confusion comes from treating CDC as if it were a business API. It is not. CDC tells subscribers that Salesforce data changed. If subscribers should react to a meaningful business step, define that step explicitly as a Platform Event.

Code examples

These examples are intentionally simple and map to the documented event model. The Platform Event examples use a custom event contract. The CDC example shows how change subscribers work with ChangeEventHeader.

Platform Event publisher in Apex

Use Platform Events when you want to publish a business fact that is independent from the internal shape of the source record.

public with sharing class OrderEventPublisher {
    public static void publishSubmitted(Order orderRecord) {
        Order_Submitted__e eventRecord = new Order_Submitted__e(
            Order_Id__c = String.valueOf(orderRecord.Id),
            Account_Id__c = String.valueOf(orderRecord.AccountId),
            Order_Number__c = orderRecord.OrderNumber,
            Status__c = 'Submitted',
            Submitted_At__c = System.now()
        );

        Database.SaveResult publishResult = EventBus.publish(eventRecord);

        if (!publishResult.isSuccess()) {
            for (Database.Error err : publishResult.getErrors()) {
                System.debug(LoggingLevel.ERROR,
                    err.getStatusCode() + ': ' + err.getMessage());
            }
        }
    }
}

The event schema is yours. That is why Platform Events are a better contract when subscribers should not care whether the source process touched one object, three objects, or a dozen fields behind the scenes.

Platform Event subscriber with an Apex trigger

Platform Event triggers are always after insert because the event has already been published onto the bus.

trigger OrderSubmittedSubscriber on Order_Submitted__e (after insert) {
    List<Task> followUps = new List<Task>();

    for (Order_Submitted__e eventRecord : Trigger.New) {
        followUps.add(new Task(
            Subject = 'Process submitted order ' + eventRecord.Order_Number__c,
            Status = 'Not Started',
            Priority = 'Normal',
            Description =
                'Order Id: ' + eventRecord.Order_Id__c +
                '\nAccount Id: ' + eventRecord.Account_Id__c +
                '\nSubmitted at: ' + String.valueOf(eventRecord.Submitted_At__c)
        ));
    }

    if (!followUps.isEmpty()) {
        insert followUps;
    }
}

CDC subscriber with an Apex trigger

CDC is different. You do not publish the change event yourself. You enable the object for CDC in Setup, and Salesforce inserts change events when the tracked record changes.

trigger AccountChangeSubscriber on AccountChangeEvent (after insert) {
    for (AccountChangeEvent changeEvent : Trigger.New) {
        EventBus.ChangeEventHeader header = changeEvent.ChangeEventHeader;

        if (header.changeType == 'UPDATE') {
            System.debug(LoggingLevel.INFO,
                'Record ids: ' + header.recordIds +
                ', transaction key: ' + header.transactionKey +
                ', changed fields payload: ' + header.changedFields +
                ', current Name value: ' + changeEvent.Name);
        }
    }
}

Notice the contract difference. In CDC, the subscriber reasons about changeType, recordIds, and the source-object fields carried on the event. That is great for data synchronization and change processing, but it is a different abstraction than a business-domain event.

External topic naming with Pub/Sub API

Salesforce's Pub/Sub quick starts document the topic naming rules clearly, which is helpful when comparing the two models from an integration-client perspective.

Custom Platform Event: /event/Order_Submitted__e
Standard-object CDC:  /data/AccountChangeEvent
Custom-object CDC:    /data/Shipment__ChangeEvent
All tracked CDC:      /data/ChangeEvents

Delivery, replay, and payload details

Salesforce documents the event bus as a time-ordered event log. Platform Events and CDC events are stored on that bus for 72 hours. A subscriber can persist the last processed replay position and ask for missed events again, as long as that replay point is still inside the retention window.

Important: replay IDs are opaque and Salesforce does not guarantee they are contiguous. Store them exactly as received and treat them as bookmarks, not sequence numbers.
  • Store replay positions durably: never keep them only in memory if you care about reconnect recovery.
  • Separate event meaning from transport: Platform Events vs CDC is an architecture decision; Pub/Sub API vs Streaming API is a transport decision.
  • Decode CDC deltas correctly: Salesforce documents changedFields, diffFields, and nulledFields on CDC headers.
  • Know the Pub/Sub payload shape: Pub/Sub API CDC messages include unchanged fields with empty values, while changedFields tells you what actually changed.

That last point is easy to miss and matters a lot. If you inspect a Pub/Sub CDC message and assume every field shown in the payload changed, your subscriber logic will be wrong. The documented delta indicators in ChangeEventHeader are the reliable guide.

Decision framework

If your scenario is...Start with...Why
"Notify downstream systems that an order was submitted."Platform EventsYou want a business milestone with a publisher-owned contract.
"Keep a warehouse or MDM system in sync with Salesforce record changes."CDCYou want automatic notifications tied to actual CRUD mutations.
"Expose only selected record changes, not every update."Platform EventsCDC publishes tracked changes automatically; Platform Events let you filter meaning before publication.
"Let subscribers understand exactly which fields changed on a record."CDCThe change-event model and header semantics are built for that.
"Keep external contracts stable even if the internal data model changes."Platform EventsThe event schema is decoupled from the underlying object design.

In mature orgs, the answer is often both. CDC can feed operational sync and analytics, while Platform Events express curated business milestones such as Customer_Tier_Changed__e or Invoice_Approved__e.

Common mistakes

  • Using CDC as a business-domain API: that couples subscribers to your object model and CRUD behavior instead of a stable process contract.
  • Using Platform Events as a raw replication stream: that usually recreates CDC badly and forces you to hand-maintain an object-shaped contract.
  • Ignoring replay storage: both event types are replayable only if your subscriber persists replay IDs and reconnect logic correctly.
  • Forgetting CDC enablement: no object selection in Setup means no CDC events, even if the subscriber code is ready.
  • Assuming one event per trigger execution: Salesforce event triggers still need bulk-safe logic, and the documented Apex trigger batch size for Platform Events and CDC is larger than standard object-trigger batches.
  • Misreading CDC payloads in Pub/Sub API: use changedFields, nulledFields, and diffFields instead of assuming every field shown changed.

Recommendation

Use Platform Events when you want Salesforce or an external publisher to announce a business event intentionally, with a clean contract that can survive internal data-model changes. Use CDC when you want subscribers to react to Salesforce record mutations themselves.

If you are still unsure, choose based on what the subscriber is supposed to understand. If the subscriber should understand business intent, publish a Platform Event. If the subscriber should understand which Salesforce record changed and how, use CDC. Then consume either one through Pub/Sub API when you want a single external interface and replay-aware subscriber design.

References