A Salesforce Apex trigger framework where an object's Apex trigger actions are configured via custom metadata.
The trigger framework uses an unlocked package for distribution. To install it, use the appropriate installation url based on your org type using the instructions below.
- Open installation page.
- Login with your credentials. The "Admins Only" option is fine.
- Click Install.
- Open installation page.
- Login with your credentials. The "Admins Only" option is fine.
- Click Install.
To create a trigger action for an Object, the steps are to create the trigger action class, create the trigger for the object, and create the custom metadata records. Separate each trigger "automation" into one trigger action.
Create a global Apex class that extends the mtf.TriggerAction
apex class and override the appropriate trigger event methods as needed. If an event isn't needed, don't override it and the mtf.triggeraction's empty, no-op event method will be executed instead. This allows only the event methods needed to be used.
In the overridden event method, downcast the provided list or map to its strongly-typed SObject type for better compile time assistance.
Note: It has to be a global Apex class so the framework can see it since the framework uses Type.forName to dynamically instantiate it.
void beforeInsert(List<Sobject> newRecords)
void beforeUpdate(Map<Id, Sobject> oldRecordsMap, Map<Id, Sobject> newRecordsMap)
void beforeDelete(Map<Id, Sobject> deletedRecordsMap)
void afterInsert(List<Sobject> newRecords)
void afterUpdate(Map<Id, Sobject> oldRecordsMap, Map<Id, Sobject> newRecordsMap) { }
void afterDelete(Map<Id, Sobject> deletedRecordsMap) { }
void afterUndelete(List<Sobject> undeletedRecords) { }
Let's say one has to do some custom validation when Accounts are inserted.
global with sharing class AccountCustomValidation extends mtf.TriggerAction {
public override void beforeInsert(List<SObject> newRecords) {
List<Account> newAccounts = (List<Account>) newRecords;
for (Account newAccount : newAccounts) {
// Add newAccount custom validation here
}
}
}
Create an Apex Trigger for the SObject if there's none. If this already exists, skip this because there should only be one Apex Trigger for the SObject. All the trigger events should be specified for the trigger so one doesn't have to remember to update it later as new events are needed. The trigger then instantiates the mtf.TriggerHandler
class and invokes its run method.
trigger AccountTrigger on Account (before insert, before update, after insert, after update, before delete, after delete, after undelete) {
new mtf.TriggerHandler().run();
}
One creates one "sObject Trigger Setting" custom metadata record per SObject and then one "Trigger Action" custom metadata record for each Trigger Action to invoke for an SObject.
The "sObject Trigger Setting" custom metadata records tell the framework which SObjects have trigger actions for them and if they are active or not. If the SObject already has an "sObject Triger Setting" record, skip this step since only 1 should be created per SObject.
Steps to create the record
- Open Setup in the browser.
- Open the "Custom Metadata Types" page.
- Click "Manage Records" next to "sObject Trigger Setting".
- Click the "New" button.
- Enter the SObject's API Name for the Label, "sObject Trigger Setting Name", and "Object API Name" fields.
- Click Save.
The "Trigger Action" custom metadata record tells the framework the Apex Classes to execute for a given SObject. One record is created per apex class.
Steps to create the record
- Open Setup in the browser.
- Open the "Custom Metadata Types" page.
- Click "Manage Records" next to "Trigger Action".
- Click the "New" button.
- Enter
- Label - Trigger Action Name
- Trigger Action Name - The Label but with underscores instead of spaces.
- sObject Trigger Seeting - The related "sObject Trigger Setting" to indicate which SObject this trigger action is for.
- Apex Class Name - The name of the global Apex class that extends
mtf.TriggerAction
. - Order - The numeric order in which this trigger action will run.
- Description - A description of the functionality this trigger action provides. This is required.
- Click Save.
One can bypass trigger actions from executing using the custom metadata or programatically. All trigger actions for a given object can be bypassed or specific trigger actions can be bypassed.
There are two options to bypass all of an object's trigger actions:
On the sObject Trigger Setting custom metadata record in Setup, uncheck the Active checkbox. This disables all trigger actions from executing for that sObject for everyone. To reenable them, check the Active checkbox again.
One can also bypass all trigger actions for an object programmatically by using the mtf.SObjectTriggerSetting.bypass
method and passing in the SObject's API Name. This disables that object's trigger actions for this execution context only. Use the mtf.SObjectTriggerSetting.clearBypass
method and pass in the SObject's API Name to clear the bypass.
mtf.SObjectTriggerSetting.bypass('Account');
mtf.SObjectTriggerSetting.clearBypass('Account');
To disable one or more specific trigger actions for an object, one can use custom metadata or programmatically with Apex.
On the Trigger Action custom metadata record(s) in Setup, uncheck the Active checkbox for those trigger actions to disable. This prevents the trigger action from running for everyone. To renable them, check the Active checkbox again.
One can also bypass a specific trigger action programmatically by using the mtf.TriggerActionSetting.bypass
method and passing in the Apex Class Name that extends the mtf.TriggerAction
. This disables the trigger action for this execution context only. Use the mtf.TriggerActionSetting.clearBypass
method and pass in the Apex Class Name to clear the bypass.
mtf.TriggerActionSetting.bypass('NoOpTriggerAction');
mtf.TriggerActionSetting.clearBypass('NoOpTriggerAction');
This framework uses custom metadata to determine which Apex Classes, and in which order, to run for an SObject's Apex Trigger. This allows more granular control over the automation by an admin or developer. It also allows multiple implementers to develop multiple trigger actions concurrently more easily since the code is separated into different apex classes.
- More granularity and control was the primary goal for this framework over my Simple Trigger Framework. It doesn't provide recursion control / prevention since I believe that is better controlled by each trigger action so that allows the framework to be simpler.
- Decoupled Trigger Actions.
- Easy Distribution Through Unlocked Package.
Used in Apex Triggers to run the specified trigger actions configured for that Object. It uses the TriggerActionProvider to determine which Trigger Actions to execute for each trigger event.
Used to determine the TriggerActions to run for a given SObject using the SObjectTriggerSettings and their TriggerActionSettings. One can provide it an SObjectTriggerSettingQuerier to use to fetch its SObject Trigger Settings. By default the MetadataSObjectTriggerSettingQuerier is used. The test code uses the TestSObjectTriggerSettingQuerier to build the SObjectTriggerSettings and their TriggerActionSettings in memory so it doesn't depend on the custom metadata.
One could build another SObjectTriggerSettingQuerier if the settings are stored elsewhere such as in Objects.
This class has the trigger action settings for an SObject and indicates if it's active or not. One can also programmatically bypass all the SObject's trigger actions using the bypass and clearBypass methods.
This class has the setting information for a trigger action of an SObject. It knows how to instantiate the TriggerAction using the specified ApexClass. One can also programmatically bypass all trigger action using the bypass and clearBypass methods.
The base class for all trigger actions. It has one virtual, empty method for each trigger event. One overrides the method to use in the sub-class and leaves the unused ones in the base class here.
Initial release. Installation URL: https://login.salesforce.com/packaging/installPackage.apexp?p0=04t4W0000030AMJQA2
I reviewed the Apex Trigger Action Framework and thought it was good overall but a bit too granular where one implements a trigger action per trigger event. This made me wonder what if I created the Trigger Action framework but not as granularly and without recursion prevention.
The Salesforce community has many Apex trigger frameworks available. If this one doesn't fit your needs, here are some others that might along with other resources you may find helpful: