mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-07-30 11:43:26 +08:00
* Migrating Core Salesforce Skills * Updating pr comments * updat reference * Updating a skill * Migrating Datacloud skills * Migrating Industries cloud skills * Validating - skills fixing --------- Co-authored-by: Sandip Kumar Yadav <sandipkumar.yadav+sfemu@salesforce.com>
140 lines
5.3 KiB
Plaintext
140 lines
5.3 KiB
Plaintext
/**
|
|
* @description Change Data Capture (CDC) Subscriber Trigger for {{ObjectName}}
|
|
*
|
|
* Use Case: React to data changes in near real-time
|
|
* - Sync data to external systems
|
|
* - Audit logging
|
|
* - Cache invalidation
|
|
* - Event-driven integrations
|
|
*
|
|
* CDC Channel Name: {{ObjectName}}ChangeEvent
|
|
* - Standard objects: AccountChangeEvent, ContactChangeEvent, etc.
|
|
* - Custom objects: MyObject__ChangeEvent (append "ChangeEvent" to API name)
|
|
*
|
|
* Key Concepts:
|
|
* - ChangeEventHeader contains metadata (changeType, changedFields, recordIds)
|
|
* - Event contains changed field values (nulls for unchanged fields)
|
|
* - Supports CREATE, UPDATE, DELETE, UNDELETE change types
|
|
* - Gap events indicate missed events (handle replay)
|
|
*
|
|
* IMPORTANT:
|
|
* - Enable CDC for object in Setup → Integrations → Change Data Capture
|
|
* - Triggers fire in separate transaction from DML
|
|
* - Events are retained for 3 days (replay window)
|
|
*
|
|
* @author {{Author}}
|
|
* @date {{Date}}
|
|
*/
|
|
trigger {{ObjectName}}CDCSubscriber on {{ObjectName}}ChangeEvent (after insert) {
|
|
|
|
// Track replay ID for checkpoint
|
|
String lastReplayId = '';
|
|
|
|
for ({{ObjectName}}ChangeEvent event : Trigger.new) {
|
|
// Store replay ID
|
|
lastReplayId = event.ReplayId;
|
|
|
|
// Get change event header (metadata about the change)
|
|
EventBus.ChangeEventHeader header = event.ChangeEventHeader;
|
|
|
|
// Extract header information
|
|
String changeType = header.getChangeType();
|
|
List<String> changedFields = header.getChangedFields();
|
|
List<String> recordIds = header.getRecordIds();
|
|
String entityName = header.getEntityName();
|
|
Long commitNumber = header.getCommitNumber();
|
|
Datetime commitTimestamp = header.getCommitTimestamp();
|
|
String transactionKey = header.getTransactionKey();
|
|
|
|
// Log event details
|
|
System.debug(LoggingLevel.DEBUG,
|
|
'CDC Event - Type: ' + changeType +
|
|
', Entity: ' + entityName +
|
|
', Records: ' + recordIds +
|
|
', Changed Fields: ' + changedFields);
|
|
|
|
try {
|
|
// Route based on change type
|
|
switch on changeType {
|
|
when 'CREATE' {
|
|
{{ObjectName}}CDCHandler.handleCreate(event, header);
|
|
}
|
|
when 'UPDATE' {
|
|
{{ObjectName}}CDCHandler.handleUpdate(event, header, changedFields);
|
|
}
|
|
when 'DELETE' {
|
|
{{ObjectName}}CDCHandler.handleDelete(recordIds, header);
|
|
}
|
|
when 'UNDELETE' {
|
|
{{ObjectName}}CDCHandler.handleUndelete(event, header);
|
|
}
|
|
when 'GAP_CREATE', 'GAP_UPDATE', 'GAP_DELETE', 'GAP_UNDELETE' {
|
|
// Gap events indicate missed events
|
|
// Should trigger full sync for affected records
|
|
System.debug(LoggingLevel.WARN,
|
|
'GAP event detected - some events may have been missed. ' +
|
|
'Type: ' + changeType + ', Records: ' + recordIds);
|
|
{{ObjectName}}CDCHandler.handleGap(recordIds, changeType);
|
|
}
|
|
when 'GAP_OVERFLOW' {
|
|
// Too many changes to track - full sync needed
|
|
System.debug(LoggingLevel.ERROR,
|
|
'GAP_OVERFLOW - full sync required for ' + entityName);
|
|
{{ObjectName}}CDCHandler.handleOverflow(entityName);
|
|
}
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
// Log error but continue processing
|
|
System.debug(LoggingLevel.ERROR,
|
|
'CDC processing error for ' + recordIds + ': ' + e.getMessage());
|
|
System.debug(LoggingLevel.ERROR, 'Stack: ' + e.getStackTraceString());
|
|
}
|
|
}
|
|
|
|
// Set resume checkpoint for durability
|
|
if (String.isNotBlank(lastReplayId)) {
|
|
EventBus.TriggerContext.currentContext().setResumeCheckpoint(lastReplayId);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ============================================================================
|
|
* CDC TRIGGER BEST PRACTICES
|
|
* ============================================================================
|
|
*
|
|
* 1. ENABLE CDC FOR OBJECT
|
|
* Setup → Integrations → Change Data Capture → Select Objects
|
|
*
|
|
* 2. HANDLE ALL CHANGE TYPES
|
|
* - CREATE: New record
|
|
* - UPDATE: Record modified
|
|
* - DELETE: Record deleted
|
|
* - UNDELETE: Record restored from recycle bin
|
|
* - GAP_*: Events were missed (sync required)
|
|
* - GAP_OVERFLOW: Too many changes (full sync needed)
|
|
*
|
|
* 3. USE CHANGEDEVENTHEAD FOR METADATA
|
|
* - getChangeType(): Operation type
|
|
* - getChangedFields(): List of changed field API names
|
|
* - getRecordIds(): Affected record IDs
|
|
* - getCommitTimestamp(): When change occurred
|
|
*
|
|
* 4. FIELD VALUES IN EVENT
|
|
* - Changed fields have new values
|
|
* - Unchanged fields are NULL
|
|
* - Use changedFields list to know what changed
|
|
*
|
|
* 5. IDEMPOTENT HANDLERS
|
|
* - Same event might fire multiple times
|
|
* - Use transactionKey to detect duplicates
|
|
* - Design handlers to be safe for replay
|
|
*
|
|
* 6. BULK CONSIDERATIONS
|
|
* - Multiple records can be in single event (batch DML)
|
|
* - Use getRecordIds() to get all affected IDs
|
|
* - Process all record IDs efficiently
|
|
*
|
|
* ============================================================================
|
|
*/
|