When a user presses save in the user interface, a lot happens in Salesforce – validation rules kick in, triggers are executed, flows are run, assignment rules are executed, and much more. This can all add up to a lot of logic being executed while that user is waiting for the application to respond again. Typically, not all of that logic needs to be ran immediately and can be processed at a later time, increasing the performance of the transaction.
In this blog post, we’ll look at a way to offload some of that logic from the save event with a combination of clicks and code. This way, you’ll have the benefit of being able to build your processes quickly using Flow and your users will have more responsive interactions with Salesforce.
Decoupling the transaction
In order to decouple the transaction, there are a few capabilities of Salesforce that you need to get to know. Together, these can help you improve the user experience and save-time.
Change event Apex triggers
In the Summer ’19 release, Salesforce introduced Change event Apex triggers to the platform. While you may already be familiar with triggers, they allow you run Apex each time a record is inserted, updated, or deleted. Triggers are something we’ve already had for a long time in Salesforce. A standard trigger runs in the same transaction context that initiated it, which works well in most cases – but lengthy processes and integrations can lead to delays in the user interface. This is where change event Apex triggers come in. With change event Apex triggers, you’re not placing a trigger on the actual object (like Account), but on the Change Data Capture event that results from a change to that object.
Change Data Capture
Change Data Capture (CDC) events are similar to Platform Events in the way that they are sent and received. The main difference is that CDC events are fired each time data changes are made to standard or custom objects. Each CDC event contains information like the object type, the fields changed, and the user who made the changes. In Setup, you can specify which objects should publish CDC events. CDC Events are published asynchronously, which decouples them from the code running from the database transaction. This sets the groundwork for creating a faster save-time.
Salesforce Flow is the tool to automate functionality in the Salesforce platform. Flows can either be Screen Flows, guiding a user through a process in the user interface; or they can be an Autolaunched Flow, performing certain tasks in the platform without any user interaction. Autolaunched Flows can be started from a record change or a Platform Event. They can also be scheduled or called as a subflow by another flow. However, CDC Events cannot trigger an Autolaunched Flow. This does require a bit of code to bring it all together, so let’s take a look at how we can combine the low code power of Flow with these events and triggers.
Passing the event data into the flow
When a Change Data Capture event triggers, it passes a lot of information into an Apex class. The sObject created is an EventBus.ChangeEventHeader object that cannot pass this object to the flow as an input variable directly. The first step is to create an Apex class that can be used for an Apex-defined data type that we can use instead as its Flow input variable. That class is very straightforward, because it simply needs to have all the data elements that are available in the
ChangeEventHeader class but now in a form that Flow can handle.
For this, we’ve created the
CDCTriggerInput class in Apex. In addition to the header information, we also want to store the actual values of the changed fields. The best type of structure to hold that data is inside of a Map in Apex. Now this is something that the flow will not be able to directly use, but that’s something we’ll fix later with a helper component for the flow.
The class variables have the @AuraEnabled annotation to make sure that they’re visible in the flow later on.
Starting the flow from the Apex trigger
In order to start the flow from this trigger, there are a few things that we must do first:
- Copy the event header data to the
- Copy the changed field values to the
- Pass the inputs to the right flow
To do this, you can create a generic Apex class. In this case, we’ll use the
InitateFlowFromCDC class. This class only has one responsibility: running the flow. As for input for the method, we pass in the API name of the flow, the Change Event Header Object, and the sObject. The code looks as follows:
Writing the Change Data Capture event Apex trigger
Now that we’ve created the above classes. we can enable CDC on an object and then create a trigger to start the process. In this following example we use the Account object and create the following Apex trigger for the Account CDC object.
What we see in this trigger code is that we follow more or less the same structure as with any trigger. Do note that Event Triggers are always
after insert. The trigger is on the event and not on the original data (an update of the original data leads to a new event that gets inserted telling you about the update).
Event triggers need to be bulkified, so you will have to assume that there is a list of events that was passed to the trigger. As you loop through these events, you take the
ChangeEventHeader for each event and pass it to the
InitiateFlowFromCDC class together with the API name of the flow that needs to get executed (in this example
The_Flow_That_Got_Called_From_CDC) and the event itself.
Building your flow
Creating your Input parameter and using it
With the base code in place, the flow is relatively straightforward. The most important thing to note is that the flow needs to have an input variable of type Apex-Defined, using the
CDCTriggerInput Apex class. The variable name should be
TriggerInput as defined earlier.
Once we’ve defined the variable, the first thing you want to do is check which type of event has triggered the action. Was data inserted, updated, deleted or undeleted? We can do this by checking the
changeType property of the
changeType can be of multiple values:
GAP_CREATEindicates that a record has been created
GAP_UPDATEindicates that a record has been updated
DELETEindicates that a record has been deleted
UNDELETEindicates that a record has been undeleted
Next, you’ll also want to identify which record(s) triggered this event. As we mentioned, this could be triggered in bulk. You can use another variable in the flow to store the record Ids of any records passed in from the trigger. You want to make sure that you enable this to be stored as a collection just incase multiple records we’re passed in via a mass update. We will need to assign the
recordIds from the
TriggerInput into the new property and loop through them to properly assign them to the new property in the flow.
In a similar way, you can loop through the
TriggerInput.changedFields list to determine which fields were changed in case of an an update – in all other use cases this value is empty.
Getting the changed field values
Getting the actual changed field values is bit more complicated. The field values are part of
CDCTriggerInput structure which is stored in a map. Unfortunately, flows cannot handle maps so we must use the
CDCTriggerInputHelper class that is called from the flow to get the values from the
Using this in Flow, you specify (as input) the CDCTriggerInputHelperRequest variable that you get from looping through the changed fields. Then have separate Flow variables available of the various types that can store the actual value. By checking the fieldType variable, you can find out which of the other variables will contain the actual value.
Once completed, the flow is actually quite simple!
You can see the first assignment step takes the
recordIds and adds them to the to local variables. Next we loop through the
recordids, and inside that loop we loop through all the changed fields for that record. We use the Get Trigger Field Value Apex Action to get the field value, and finally we call the Sync Account action that does the actual heavy work which was the reason for this solution in the first place.
In this blog post we’ve looked at how with some straightforward generic Apex classes we can make it possible to have flows run off Change Data Capture events, thereby decoupling them from the database transaction and not impacting the performance of the user in the user interface.
If you don’t have any experience with Change Data Capture yet, then have a look at the Change Data Capture Basics module on Trailhead.
And if you’re not yet familiar with Flow, then the ‘Automate Your Business Process with Lightning Flow’ trail on Trailhead is a great starting point.
About the author
Jack van Dijk is a Technical Architect working for Salesforce in the Netherlands. He has now more than four years experience in Salesforce and over 20 years in the CRM world. Jack can be reached at https://www.linkedin.com/in/jackvandijk/