Change Data Capture represents the changes in salesforce records. It acts as a real-time notification system for changes in Salesforce, allowing external databases to stay up-to-date. Receive near-real-time changes of Salesforce records, and synchronize corresponding records in an external data store. Change may be like create, update, delete and undelete of the record.
CDC are available in salesforce classic, Lightning Experience and also available in Enterprise, Performance, Unlimited, and Developer editions.
The CDC Salesforce securely sends real-time updates (events) to other systems, even for millions of changes daily. These systems can access past updates for 3 days. Everything is encrypted to keep your data safe.
Select Objects for Change Notifications in the User Interface
To receive notification on the default standard channel for record change choose the custom object or standard object for the change data capture.
Go to setup, In the Quick find box enter the Change Data Capture and Click on Change Data Capture.
Here will show object lists. From the Available Entities List select the object according to a requirement (Note : You can select up to 5 entities including custom and standard objects. To enable more entities, contact your Salesforce Account Representative to purchase an add-on license).
Here is LWC example:
changeDataCapture.html
<template>
<lightning-card title="Employees">
<div class="slds-p-around_small">
<div style="height: 300px;">
<lightning-datatable key-field="id" data={employeeData} columns={columns}></lightning-datatable>
</div>
</div>
</lightning-card>
</template>
changeDataCapture.html.js
import { LightningElement, api, wire, track } from 'lwc';
import { subscribe, unsubscribe, onError} from 'lightning/empApi';
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import { notifyRecordUpdateAvailable } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import getEmployees from '@salesforce/apex/ChangeDataCaptureController.getEmployees';
const COLUMNS = [
{ label: 'Name', fieldName: 'Name' },
{ label: 'FirstName', fieldName: 'First_Name__c'},
{ label: 'LastName', fieldName: 'Last_Name__c'},
{ label: 'Tenure', fieldName: 'Tenure__c'}
]
export default class ChangeDataCapture extends LightningElement {
@api recordId;
columns = COLUMNS;
@track employeeData;
channelName = '/data/Employee__ChangeEvent';
subscription = {};
connectedCallback() {
this.registerErrorListener();
this.handleSubscribe();
}
employeesActivity;
@wire(getEmployees)
wiredGetEmployees(value){
this.employeesActivity = value;
const {data, error} = value;
if(data) {
this.employeeData = data;
} else if(error) {
this.toastMessage("Error", error.body.message, "error");
}
}
handleSubscribe() {
const messageCallback = (response) => {
console.log('New message received: ');
this.handleCaptureDataChange(response);
};
subscribe(this.channelName, -1, messageCallback).then((response) => {
console.log('Subscription request sent to: ',JSON.stringify(response.channel));
this.subscription = response;
});
}
handleUnsubscribe() {
unsubscribe(this.subscription, (response) => {
console.log('unsubscribe() response: ', JSON.stringify(response));
});
}
handleCaptureDataChange(response) {
console.log('----handleCaptureDataChange----');
if(response.hasOwnProperty("data")){
if(response.data.hasOwnProperty("payload")){
const payload = response.data.payload;
//Refresh the data in own system and also in external system
if(payload.ChangeEventHeader.changeType == 'UPDATE'){
refreshApex(this.employeesActivity);
this.toastMessage("Success", "Record is updated", "success");
}
console.log(`${response.data.payload.Name}, ${response.data.payload.First_Name__c}, ${response.data.payload.Last_Name__c}, ${response.data.payload.Tenure__c}`);
}
}
}
registerErrorListener() {
onError((errors) => {
console.log('Received error from server: ', JSON.stringify(errors));
this.toastMessage("Error", errors.error, "error");
});
}
toastMessage(title, message, variant) {
this.dispatchEvent(
new ShowToastEvent({
title: title,
message: message,
variant: variant,
mode: 'dismissible'
}),
);
}
disconnectedCallback() {
this.handleUnsubscribe();
}
}
changeDataCapture.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
</targets>
</LightningComponentBundle>
Here is a sample of subscribed data receive message :
{
"data": {
"schema": "8p3YM3UaAJcr6hsjct71Yw",
"payload": {
"LastModifiedDate": "2024-05-07T08:19:00Z",
"ChangeEventHeader": {
"commitNumber": 1277335655194,
"commitUser": "0052w00000GHr0WAAT",
"sequenceNumber": 1,
"entityName": "Employee__c",
"changeType": "UPDATE",
"changedFields": [
"Name",
"LastModifiedDate",
"First_Name__c",
"Last_Name__c",
"Tenure__c"
],
"changeOrigin": "com/salesforce/api/soap/60.0;client=SfdcInternalAPI/",
"transactionKey": "000a4cbc-55bd-9461-abca-3d79fe804e6a",
"commitTimestamp": 1715069940000,
"recordIds": [
"a062w00000kFFPhAAO"
]
},
"First_Name__c": "Daniel",
"Tenure__c": 1,
"Name": "Daniel Smith",
"Last_Name__c": "Smith"
},
"event": {
"replayId": 17346826
}
},
"channel": "/data/Employee__ChangeEvent"
}
Standard Channel for All Selected Entities /data/ChangeEvents Single-Entity Channel for a Standard Object /data/ChangeEvent For example, the channel to subscribe to change events for Account records is: /data/AccountChangeEvent Single-Entity Channel for a Custom Object /data/__ChangeEvent For example, the channel to subscribe to change events for Employee__c custom object records is: /data/Employee__ChangeEvent
Change events, like platform events, are stored for a short time (3 days) in a temporary holding area. You can access these messages later if needed. Each message has a unique ID that lets you replay the stream of events starting from that specific point.
A subscription channel is stream of change events that corresponds one or more entities. You can subscribe the channel to receive change event notification for record create, update, delete and undelete operations. Change data capture provides pre-defined standard channels and you can create own custom channel. The channel name is case-sensitve.
Standard channel for all selected entities:
/data/ChangeEvents
Single-Entity channel for standard object:
/data/AccountChangeEvent
Single-Entity channel for custom object:
/data/Employee__ChangeEvent
changeDataCaptureController.cls
public with sharing class ChangeDataCaptureController {
@AuraEnabled(cacheable=true)
public static List<Employee__c> getEmployees(){
try {
return [SELECT Id, Name, First_Name__c, Last_Name__c, Tenure__c FROM Employee__c];
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
Overall, CDC offers a versatile approach to data management, promoting real-time insights, efficient workflows, and robust data governance. However, it's important to consider potential drawbacks like resource consumption and implementation complexity when making a decision. Change Data Capture (CDC) in Salesforce is like having a watchful eye over your data, alerting you whenever something changes. It's a feature that tracks alterations in your Salesforce data and then sends out notifications about those changes. Think of it as having a guardian that keeps you informed about any updates, additions, or deletions in your data, helping you stay on top of things and make informed decisions.