Triggers are Apex scripts that execute before or after data manipulation events, such as before or after records insert, update, or delete. They are written to perform tasks that can’t be done by using point-and-click tools in Salesforce.
But writing ✍️ Trigger in proper way with proper design makes it more sensible and will avoid bringing blockers/big issues in future.
For this there are various frameworks followed while implementing Trigger on any object.
In this post I will explain how to use Apex Trigger Handler Framework. Its a very popular and commonly used framework.
Note: All the frameworks are good, you need to take decision based on the application need.
Salesforce Trigger Handler Framework
One Trigger per Object, this you should always keep in mind before even think about writing any Trigger. If you write more than one Trigger in one object then you wont be in a position to decide which one will run 1st. A simple Trigger Handler Framework can control the order of execution and mitigate this issue.
One more problem is if you want to Deactivate the trigger in Production, then its impossible to do. To overcome this problem you need to implement Switch in the trigger.
Here are some key components of an Apex trigger framework:
- Trigger
- Trigger Switch
- Trigger Handler
- Trigger Helper
For example lets take Account object.
Before we start writing Trigger, lets 1st configure Trigger Switch.
- Create a Custom Metadata: First, create a custom metadata to store the configuration for turning triggers on or off. Let’s call it
Trigger_Configuration__c. This custom metadata should have a checkbox field (e.g.,IsTriggerEnabled__c) that you can use to control whether the trigger should execute. - Configure the Trigger Configuration: In Salesforce, set the value of
IsTriggerEnabled__cto true or false to enable or disable the trigger. You can use Name field to store Name of the Trigger. - Modify Your Trigger to Check the Configuration: In your trigger, you can check the value of the
IsTriggerEnabled__cfield on theTrigger_Configuration__ccustom metadata. If the field is set to true, the trigger logic will execute; otherwise, it will be skipped.
As switch is configured, let’s start creating the one and only Trigger on Account Object:
// Trigger: AccountTrigger.trigger
trigger AccountTrigger on Account ( before insert,
before update,
after insert,
after update,
before delete,
after delete) {
// Query the Trigger_Configuration__c to check if the trigger should be enabled
Trigger_Configuration__c triggerConfig = Trigger_Configuration__c.getInstance('AccountTriggerConfig'); // Assuming you've created a specific record for this trigger
if (triggerConfig != null && triggerConfig.IsTriggerEnabled__c) {
// The trigger is enabled; proceed with the trigger logic
if (Trigger.isBefore) {
if (Trigger.isInsert) {
AccountTriggerHandler.handleBeforeInsert(Trigger.new);
}
if (Trigger.isUpdate) {
AccountTriggerHandler.handleBeforeUpdate( Trigger.new,
Trigger.oldMap);
}
if (Trigger.isDelete) {
AccountTriggerHandler.handleBeforeDelete(Trigger.old);
}
} else {
if (Trigger.isInsert) {
AccountTriggerHandler.handleAfterInsert(Trigger.new);
}
if (Trigger.isUpdate) {
AccountTriggerHandler.handleAfterUpdate(Trigger.new,
Trigger.old,
Trigger.newMap,
Trigger.oldMap);
}
if (Trigger.isDelete) {
AccountTriggerHandler.handleAfterDelete(Trigger.old,
Trigger.oldMap);
}
}
}
// If triggerConfig is null or IsTriggerEnabled__c is false, the trigger will be skipped
}
But you wont be able to save the Trigger, because it has reference of Trigger Handler. So now lets create the Trigger Handler for Account.
// Trigger Handler Class: AccountTriggerHandler.cls
public class AccountTriggerHandler {
public static void handleBeforeInsert(List<Account> newRecords) {
//Here I am trying to explain how you can reuse one for loop and avoid executing unnecessary lines of code
List<Account> salesLogicList = new List<Account> ();
List<Account> serviceLogicList = new List<Account> ();
for(Account acc : newRecords){
if(acc.recordTypeId == 'sales'){
salesLogicList.add(acc);
}else if(acc.recordTypeId == 'service'){
serviceLogicList.add(acc);
}
}
// Before Insert Logic
If(!salesLogicList.isEmpty()){
AccountTriggerHelper.executeSalesLogic(salesLogicList);
}
If(!serviceLogicList.isEmpty()){
AccountTriggerHelper.executeServiceLogic(serviceLogicList);
}
}
public static void handleBeforeUpdate(List<Account> newRecords, Map<Id, Account> oldMap) {
// Before Update Logic
// Example: Validate changes or perform calculations
AccountTriggerHelper.validateChanges(newRecords, oldMap);
}
public static void handleBeforeDelete(List<Account> oldRecords) {
// Before Delete Logic
// Example: Check dependencies or perform cleanup
AccountTriggerHelper.checkDependencies(oldRecords);
}
public static void handleAfterInsert(List<Account> newRecords) {
// After Insert Logic
// Example: Trigger post-insert actions
AccountTriggerHelper.triggerPostInsertActions(newRecords);
}
public static void handleAfterUpdate(List<Account> newRecords, List<Account> oldRecords, Map<Id, Account> newMap, Map<Id, Account> oldMap) {
// After Update Logic
// Example: Send notifications or log changes
AccountTriggerHelper.sendNotifications(newRecords, oldRecords);
}
public static void handleAfterDelete(List<Account> oldRecords, Map<Id, Account> oldMap) {
// After Delete Logic
// Example: Archive data or trigger related processes
AccountTriggerHelper.archiveData(oldRecords);
}
}
To Save the Handler you need to create Helper 1st. So below is an example of Helper class.
// Helper Class: AccountTriggerHelper.cls
public class AccountTriggerHelper {
public static void executeSalesLogic(List<Account> records) {
// Set default Status__c value
for (Account record : records) {
if (record.Status__c == null) {
record.Status__c = 'New';
}
}
}
public static void executeServiceLogic(List<Account> records) {
// Set default Status__c value
for (Account record : records) {
if (record.Status__c == null) {
record.Status__c = 'Green';
}
}
}
public static void validateChanges(List<Account> newRecords, Map<Id, Account> oldMap) {
// Validation logic for changes
// Example: Check if certain fields were modified
}
public static void checkDependencies(List<Account> records) {
// Dependency checks before deletion
}
public static void triggerPostInsertActions(List<Account> records) {
// Additional actions after insert
}
public static void sendNotifications(List<Account> newRecords, List<Account> oldRecords) {
// Send notifications or log changes
}
public static void archiveData(List<Account> records) {
// Archive data or trigger related processes after deletion
}
}
Note: Here I tried to make the framework more simpler and effective (should serve the purpose). But there are many way this framework can be written and improved based on the need.
Also do share your thought on this framework and which you are using.
Happy Learning 😊

