Change Data Capture - Asynchronous Trigger Processing

In Salesforce there are multiple ways of processing data Asynchronously using "Batch Apex", "Schedule Apex", "Queueable Apex", and "Future Method". In Summer '19 release, there is another feature available called "Asynchronous Apex Triggers" that can process data Asynchronously with the help "Change Data Capture".

With Change Data Capture enabled for any specific object (Custom and Standard) in Salesforce, it begins publishing events every time when a record is created or updated or deleted or undeleted. Change Data Capture supports all custom objects and for list of supported standard object, here is the Link.

Change Data Capture Setup:
In order to enable Objects for receiving change notifications, navigate to Setup-->Integrations-->Change Data Capture, select object to add and click Save.


Asynchronous Apex Trigger:
Once Change Data Capture is enabled on an object, events are published and those events can be processed Asynchronously using Asynchronous Apex Trigger which is an "after-insert" context and this is also one of the subscription modal apart from CometD.
Every change event has a below standard message structure which include both Header and Body Fields and the list of fields that included in the body will very based on event type!

{
  "data": {
    "schema": "<schema_ID>", 
    "payload": {
      "ChangeEventHeader": {
         "entityName" : "...",
         "recordIds" : "...",
         "changeType" : "...",
         "changeOrigin" : "...",
         "transactionKey" : "...",
         "sequenceNumber" : "...",
         "commitTimestamp" : "...",
         "commitUser" : "...",
         "commitNumber" : "..."
      }, 
     "field1":"...",
     "field2":"...",
     . . .
    }, 
    "event": {
      "replayId": <replayID>
    }
  }, 
  "channel": "/data/<channel>"
}

To get the specifics on each Event Header and Body Fields, go through below Salesforce documentation link's.
  • Change Event Header Fields
  • Change Event Body Fields

  • Let's go ahead and subscribe to the change events using Asynchronous Apex Trigger, before that we need to setup a debug log with "Automated Process" for trace entity type!


    My Scenario: I have two custom object's (Tournament and Contestant) and here Contestant object has a lookup relationship to Tournament object. Now I have below Asynchronous Apex Trigger on Contestant_ChangeEvent object that will handle change events to update parent record fields and to send some test email every time there is an CREATE or UPDATE event happens.

    /*
    * @Purpose  : Change Event Trigger on "Contestant__C" Object
    * 
    * @Developer : SFDC_DEV
    *
    * @Description : New Change Event Trigger
    *
    * @Modified Date : Jun-19-2019           
    */
    
    trigger ContestantChangeTrigger on reddydev2018__Contestant__ChangeEvent (after insert) {
        List<Tournament__c> lstUpdt = new List<Tournament__c>();
        Set<String> rcdIds = new Set<String>();
        List<Messaging.SingleEmailMessage> lstMail = new List<Messaging.SingleEmailMessage>();
        
        for (reddydev2018__Contestant__ChangeEvent iter: Trigger.New) {        
            List<String> recordIds = iter.ChangeEventHeader.getRecordIds();
            // add record id's the set
            rcdIds.addAll(recordIds);
            
            // get header details from eventbus
            EventBus.ChangeEventHeader header = iter.ChangeEventHeader;     
            
            System.debug('************ChangeEventHeader: ' + header);
            
            // create a new email
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();        
            List<String> sendTo = new List<String>();
            sendTo.add('reddydev2018@gmail.com');
            mail.setToAddresses(sendTo);        
            mail.setSenderDisplayName('ContestantChangeTrigger');
            
            
            // check if the changetype is CREATE or UPDATE
            if (header.changeType == 'CREATE') {
                System.debug('************Create_Event************');
                
                mail.setSubject('Hi - ContestantChangeTrigger CREATE got executed!');
                mail.setHtmlBody('Hello - Here is the change event occured on Entitiy: ' + header.changeType + ' and CommitUserId: ' + header.commitUser);
                lstMail.add(mail);            
            } else if (header.changeType == 'UPDATE') {
                System.debug('************Update_Event************');
                
                mail.setSubject('Hi - ContestantChangeTrigger UPDATE got executed!');
                mail.setHtmlBody('Hello - Here is the change event occured on Entitiy: ' + header.changeType + ' and CommitUserId: ' + header.commitUser);
                lstMail.add(mail);
            }        
        }
        
        System.debug('************' + rcdIds);
        
        if (rcdIds.size() > 0) {
            // query Contestant records with matching record ids
            for (reddydev2018__Contestant__c iter: [Select Id, reddydev2018__Contestant_Name__c, reddydev2018__Contestant_Email__c, reddydev2018__Tournament__c, reddydev2018__Tournament__r.reddydev2018__Winner_Name__c, reddydev2018__Tournament__r.reddydev2018__Winner_Email__c From reddydev2018__Contestant__c Where Id IN: rcdIds]) {
                // check if lookup Tournament record has same values
                if (iter.reddydev2018__Tournament__c != null && (iter.reddydev2018__Contestant_Name__c != iter.reddydev2018__Tournament__r.reddydev2018__Winner_Name__c || iter.reddydev2018__Contestant_Email__c != iter.reddydev2018__Tournament__r.reddydev2018__Winner_Email__c)) {
                    // assign name and email values to the lookup record
                    iter.reddydev2018__Tournament__r.reddydev2018__Winner_Name__c = iter.reddydev2018__Contestant_Name__c;
                    iter.reddydev2018__Tournament__r.reddydev2018__Winner_Email__c = iter.reddydev2018__Contestant_Email__c;
                    // add record to the list
                    lstUpdt.add(iter.reddydev2018__Tournament__r);
                }
            }
            
            // update qualifying records
            if (lstUpdt.size() > 0) {
                update lstUpdt;
            }
            
            // send email
            if (lstMail.size() > 0) {
                Messaging.sendEmail(lstMail);
            }
        }        
    }
    


    Reference Resource
  • Change Data Capture Developer Guide


  • Thank you!

    Comments

    1. Nice blog has been shared by you. it will be really helpful to many peoples who are all working under the technology.thank you for sharing this blog.
      Electronic Signature Software

      ReplyDelete

    Post a Comment

    Popular posts from this blog

    Lightning Data Service - Salesforce new Apex alternative - Part 2

    Using JavaScript Promises in Lightning Components

    Two-factor authentication using LWC