Change Data Capture 基础

掌握 Change Data Capture:实时发布 Salesforce 记录的 create/update/delete/undelete 变更事件,通过 Pub/Sub API 或异步 Apex trigger 订阅,实现事件驱动的数据同步。...

📅 2026/10/4 ✍️ ponybai 🏷️ salesforce, developer, integration, headless

学习目标

slide_2

完成本单元后,你将能够:

  • 描述什么是变更事件(change events)。
  • 解释流式技术(streaming technology)的益处。
  • 解释何时使用变更事件。

开始之前:订阅 Change Data Capture 的方式之一是 Apex trigger,所以你需要对 Apex 类、trigger 和 Apex 测试有基本了解。刚接触 Apex 的可先完成 Build Apex Coding Skills 轨迹,或 Apex Basics & Database、Apex Testing、Apex Triggers 模块。熟悉平台事件也有帮助,建议完成 Platform Events Basics 模块。

什么是 Change Data Capture?

slide_3

Change Data Capture(CDC)是 Lightning Platform 上的流式产品,让你高效地把 Salesforce 数据与外部系统集成。你可以实时接收 Salesforce 记录的变化,并在外部数据存储中同步相应记录。CDC 为 Salesforce 记录的 create、update、delete、undelete 操作发布事件。

用 CDC 更新外部系统的数据,而不是定期导出或 API 轮询。CDC 是云端实时数据复制过程中「持续同步」的那部分(阶段:第 0 天全量复制 → 新/更新数据的持续同步 → 两边重复数据的对账)。CDC 发布 Salesforce 数据的增量,需要一个集成应用来接收事件并在外部系统执行更新。

什么是流事件,为什么使用它们?

slide_4

流事件(streaming events)是一个系统(发布者)发给另一个系统(订阅者)的即时通知消息。使用发布/订阅模型,CDC 在 Salesforce 数据变化时向订阅者发送通知,通知消息发送到事件总线,客户端可用 Pub/Sub API 或 Apex trigger 订阅。事件驱动系统简化了分布式企业系统间的通信,提高可扩展性,并交付实时数据。

用事件驱动架构连接系统比用 SOAP API 或 REST API 轮询更高效——轮询时数据新鲜度取决于轮询频率,而且客户端可能过度调用导致服务器变慢。

何时使用 Change Data Capture

slide_5

假设你有一个企业资源规划(ERP)系统存储业务信息,其中部分 Salesforce 数据也重复存储在那里。为确保 ERP 数据最新,可以用变更事件把 Salesforce 记录的变更同步到 ERP 系统。

使用变更事件可以:

  • 接收 Salesforce 记录变更通知(create、update、delete、undelete)。
  • 捕获所有记录的大部分字段变化。
  • 在事件头中获取变更来源(changeOrigin),忽略客户端自身产生的变更。
  • 当多个操作属于同一事务时,用事务边界执行数据更新。
  • 使用版本化的事件 schema。
  • 以可扩展的方式订阅批量变更。
  • 访问保留最长 3 天的事件。

一个集成应用示例

slide_6

Robert Bullard 是 Get Cloudy Consulting(一家专注 CRM 实施的高科技咨询公司)的软件开发者,正在为客户开发 HR 同步应用,把 Salesforce 的 Employee__c 记录数据变更同步到外部 HR 系统。集成应用需求:复制每个新增/变更的 Employee 记录、复制所有字段、基于事务复制、失败后从保留最长 3 天的事件恢复。

Robert 决定用 CDC:应用订阅 Employee 自定义对象的通道后,每次变更都会收到含所有修改字段的通知。应用检查头字段判断变更是否可立即提交或需合并,并可检索错过的通知(因为 Salesforce 存储变更事件最长 3 天)。

了解 Change Data Capture 特性

slide_7

本单元深入 CDC 机制:对象支持、变更事件消息结构、合并/gap/overflow 事件、事件通道(标准与自定义)及所需权限。

对象支持与变更事件示例

slide_8

CDC 可为 org 中所有自定义对象和一部分标准对象(Account、Contact、Lead、User、Order、OrderItem、Product2 等)生成变更事件。要接收记录变更通知,在 Setup 的 Change Data Capture 页面选择你感兴趣的对象。

变更事件消息的 payload 包含 ChangeEventHeader 中的头字段(变更信息)以及记录字段和系统字段。头字段包括:

  • entityName:记录变更所属的标准或自定义对象名(如 Employee__c)。
  • changeType:引起变更的操作——CREATE、UPDATE、DELETE、UNDELETE。
  • changedFields:更新操作中哪些字段被改变(其他操作为空)。
  • changeOrigin:引起变更的来源(用于避免无限循环,识别是否由你的应用发起)。

事务相关头字段 transactionKey(唯一标识变更所属事务)和 sequenceNumber(事务内变更的顺序)帮助你在另一系统中维护准确的数据副本。

字段、合并事件与增强

slide_9

事件消息中包含的字段取决于操作和订阅者类型:在 Apex trigger 或 Pub/Sub API 客户端中,事件消息包含所有记录字段(无论是否为空)和系统字段。

  • 合并变更事件(Merged Change Events):为效率起见,如果同一对象类型的多条记录在一秒内发生相同变更,这些事件会合并为一个事件,recordIds 字段包含所有受影响记录的 ID。
  • 事件增强(Enrichment):选择字段增强发送给 Pub/Sub API 订阅者的变更事件,即使字段未改变也包含在消息中(如外部 ID 用于匹配)。
  • Gap 事件和 Overflow 事件:无法生成变更事件或通知订阅者错误时生成 gap 事件;变更量大时生成 overflow 事件。二者不含记录数据(gap 事件含记录 ID 便于检索数据)。

订阅事件通道

slide_10

现在了解了变更事件消息的样子,来看看如何接收。Salesforce 提供多种订阅方式:

  • Pub/Sub API:适用于 Salesforce 外部应用的高效 gRPC-based API(HTTP/2),发布和交付二进制事件消息。
  • Apex trigger:在 Lightning 平台上异步处理数据变更。
  • empApi Lightning 组件:在 Lightning 平台应用内即时接收数据变更通知。

订阅通道(subscription channel)是对应一个或多个实体的变更事件流。CDC 提供预定义的标准通道,你也可以创建自己的自定义通道。

订阅通道与权限

slide_11

标准通道:ChangeEvents 通道包含一个或多个已选实体的变更事件(单个流)。若只订阅单个实体,用单实体通道——标准对象如 /data/AccountChangeEvent,自定义对象如 /data/Employee__ChangeEvent。

自定义通道:有多个订阅者、每个订阅者接收不同实体集时使用;也用于事件增强,把增强字段隔离在特定通道上。自定义通道格式如 /data/SalesEvents__e。

权限:CDC 忽略共享设置,为对象的所有记录发送变更事件。订阅用户需按实体拥有相应权限。字段级安全:CDC 尊重 org 的字段级安全设置,交付的事件只包含订阅用户有权查看的字段。

订阅事件通道

slide_12

本单元动手实践:创建 Employee 自定义对象、启用 CDC、用 Pub/Sub API 订阅,并用 CRUD 操作测试。

创建并启用 Employee 对象

slide_13

先创建 Employee 自定义对象:Setup → Object Manager → Create → Custom Object,Label=Employee、Plural Label=Employees、Object Name=Employee、Record Name=Employee Name,勾选 Launch New Custom Tab Wizard,Tab Style 选 Building。再创建字段:Last Name(Text,Length 50,Required)、First Name(Text,Length 50)、Tenure(Number,Length 18,Decimal 0)。

启用 CDC:Setup → Quick Find 输入 Change Data Capture → 在 Available Entities 选 Employee (Employee__c) 点 > 箭头 → Save。

使用 Pub/Sub API 订阅

slide_14

接下来展示如何用 Pub/Sub API Java 客户端订阅 Employee 记录的变更事件(本节步骤不必照做也能完成徽章)。配置 arguments.yaml:LOGIN_URL、USERNAME、PASSWORD(附加安全令牌)、TOPIC=/data/Employee__ChangeEvent、PROCESS_CHANGE_EVENT_HEADER_FIELDS=true。然后运行 ./run.sh genericpubsub.Subscribe 订阅。

订阅 Employee__c 通道后,任何 Employee 记录的创建或修改都会生成通知并打印到控制台。测试 CRUD 操作:创建(Employee Name=e-100、Last Name=Smith、First Name=Patricia)→ UPDATE(改 First Name=Trish、Tenure=3,changedFields 含 First_Name__c 和 Tenure__c)→ DELETE(记录和系统字段均为空值)→ UNDELETE(通过 Apex 从回收站恢复,事件含原删除记录的字段)。

使用 Apex Trigger 订阅变更事件

slide_15

本单元用异步 Apex trigger 在 Salesforce 内部订阅变更事件,无需外部客户端。

用于变更事件的异步 Apex Trigger

slide_16

你可以在 Lightning 平台上用 Apex trigger 订阅变更事件。变更事件 trigger 与对象 trigger 类似,但有几个差异:

  • 只支持 after insert trigger。
  • 变更事件 trigger 在数据库事务完成后异步运行(不像对象 trigger 在事务内运行),适合处理资源密集的业务逻辑。
  • 在 Automated Process 实体下运行(调试日志由该实体创建,CreatedById/OwnerId 等系统字段引用 Automated Process)。
  • 受 Apex 同步 governor limits 约束,最大批量 2000 条事件消息。

Apex 变更事件消息中所有记录字段都在(未变更字段为 null,显式置为 null 的字段也为 null)。用 changedFields 头字段判断哪些字段被修改(API 版本 47.0 或更高可用)。

创建并验证变更事件 Trigger

slide_17

用 Developer Console 创建变更事件 trigger:File | New | Apex Trigger,Name=EmployeeChangeTrigger,下拉选 Employee__ChangeEvent(创建自定义对象时系统自动生成该变更事件对象),替换默认代码为:

trigger EmployeeChangeTrigger on Employee__ChangeEvent (after insert) {
  List<Task> tasks = new List<Task>();
  // Iterate through each event message.
  for (Employee__ChangeEvent event : Trigger.New) {
    // Get some event header fields
    EventBus.ChangeEventHeader header = event.ChangeEventHeader;
    System.debug('Received change event for ' + header.entityName +
      ' for the ' + header.changeType + ' operation.');
    // For update operations, get a list of changed fields
    if (header.changetype == 'UPDATE') {
        System.debug('List of all changed fields:');
        for (String field : header.changedFields) {
            if (null == event.get(field)) {
                System.debug('Deleted field value (set to null): ' + field);
            } else {
                System.debug('Changed field value: ' + field + '. New Value: ' + event.get(field));
            }
        }
    }
    // Get record fields and display only if not null.
    System.debug('Some Employee record field values from the change event:');
    if (event.First_Name__c != null) { System.debug('First Name: ' + event.First_Name__c); }
    if (event.Last_Name__c != null) { System.debug('Last Name: ' + event.Last_Name__c); }
    if (event.Name != null) { System.debug('Name: ' + event.Name); }
    if (event.Tenure__c != null) { System.debug('Tenure: ' + event.Tenure__c); }
    // Create a followup task
    Task tk = new Task();
    tk.Subject = 'Follow up on employee record(s): ' + header.recordIds;
    tk.OwnerId = header.CommitUser;
    tasks.add(tk);
  }
  // Insert all tasks in bulk.
  if (tasks.size() > 0) {
    insert tasks;
  }
}

该 trigger 遍历 Trigger.New 中的每条变更事件,读取头字段;若是 update 操作则读取 changedFields 字段列表;显示非空的记录字段值;最后为新的 Employee 记录创建后续任务(Task)。

验证:在 Setup 的 Debug Logs 为 Automated Process 实体启用调试日志(New Debug Level 命名 CustomDebugLevel)。创建 Employee(e-200、Smith、Joseph、Tenure=1)再更新(First Name 改 Joe、清空 Tenure),刷新 Debug Logs 查看 System.debug 输出——创建对应 CREATE 操作日志,更新对应 UPDATE 操作日志(含 LastModifiedDate 等变更字段)。

测试变更事件 Trigger

slide_18

本单元为变更事件 trigger 编写 Apex 测试,验证 trigger 行为并满足生产部署的代码覆盖率要求。

测试变更事件 Trigger

slide_19

测试 trigger 不仅是好习惯,也是平台强制要求——打包或部署 Apex trigger 到生产前,必须提供 Apex 测试和足够的代码覆盖率。测试方法的结构:第一句用 Test.enableChangeDataCapture(); 启用所有实体的 CDC(仅测试生效,不影响 org 的 CDC 选择);执行 DML 操作后调用 Test.getEventBus().deliver(); 把事件消息从测试事件总线投递到对应变更事件 trigger 并触发它。

测试变更事件消息发布到测试事件总线(与 Salesforce 事件总线分离),不持久化在 Salesforce,也不投递到测试类之外的事件通道。

创建并运行 Trigger 的测试

slide_20

创建测试类 TestEmployeeChangeTrigger(Developer Console → File | New | Apex Class):

@isTest
public class TestEmployeeChangeTrigger {
    @isTest static void testCreateAndUpdateEmployee() {
        // Enable all Change Data Capture entities for notifications.
        Test.enableChangeDataCapture();
        // Insert an Employee test record
        insert new Employee__c(Name='e-101',
            First_Name__c='Astro',
            Last_Name__c='Test',
            Tenure__c=1);
        // Call deliver to fire the trigger and deliver the test change event.
        Test.getEventBus().deliver();
        // VERIFICATIONS
        Task[] taskList = [SELECT Id,Subject FROM Task];
        System.assertEquals(1, taskList.size(),
            'The change event trigger did not create the expected task.');
        // Update employee record
        Employee__c[] empRecords = [SELECT Id,OwnerId,First_Name__c,Tenure__c FROM Employee__c];
        Employee__c emp = empRecords[0];
        emp.First_Name__c = 'Codey';
        emp.Tenure__c = null;
        update emp;
        // Call deliver to fire the trigger for the update operation.
        Test.getEventBus().deliver();
        // VERIFICATIONS
        Task[] taskList2 = [SELECT Id,Subject FROM Task];
        System.assertEquals(2, taskList2.size(),
            'The change event trigger did not create the expected task.');
    }
}

测试方法创建一条 Employee 测试记录并更新它,每次操作都触发 Employee 变更事件上的 trigger。测试通过查询 trigger 创建的任务并验证任务数量来确保执行。在 Developer Console 点击 Run Test,Tests 选项卡显示结果,EmployeeChangeTrigger 的代码覆盖率为 100%。你现在已经可以为变更事件 trigger 编写测试类并部署到生产了!


文章来源:Trailhead - Change Data Capture Basics