The Trigger Handler Pattern in Salesforce is a best practice for managing Apex Triggers efficiently, ensuring scalability, maintainability, and performance. Here are the best practices for implementing the Trigger Handler Pattern:
1. Use One Trigger Per Object
Avoid multiple triggers on the same object.
Use a single trigger that delegates logic to a handler class.
📌 Why? Ensures execution order control and prevents recursion issues.
2. Create a Trigger Handler Class
Move all logic from the trigger into a separate Apex class (handler).
The trigger should only call methods from the handler.
📌 Why? Improves code readability, modularity, and testability.
Example:
trigger AccountTrigger on Account (before insert, before update) {
AccountTriggerHandler handler = new AccountTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.beforeInsert(Trigger.new);
}
if (Trigger.isBefore && Trigger.isUpdate) {
handler.beforeUpdate(Trigger.new, Trigger.oldMap);
}
}
3. Use Context-Specific Methods
In the handler class, define separate methods for each trigger event.
Example handler class:
public class AccountTriggerHandler {
public void beforeInsert(List<Account> newAccounts) {
// Logic for before insert
}
public void beforeUpdate(List<Account> updatedAccounts, Map<Id, Account> oldAccountMap) {
// Logic for before update
}
}
📌 Why? Avoids cluttering a single method with multiple operations.
4. Bulkify All Operations
Always work with lists instead of single records.
Use Maps and Sets for efficiency.
Avoid SOQL inside loops and DML inside loops.
Example:
public void beforeUpdate(List<Account> updatedAccounts, Map<Id, Account> oldAccountMap) {
Set<Id> accountIds = new Set<Id>();
for (Account acc : updatedAccounts) {
accountIds.add(acc.Id);
}
Map<Id, CustomObject__c> relatedData = new Map<Id, CustomObject__c>(
[SELECT Id, Name FROM CustomObject__c WHERE Account__c IN :accountIds]
);
for (Account acc : updatedAccounts) {
if (relatedData.containsKey(acc.Id)) {
acc.Some_Field__c = relatedData.get(acc.Id).Name;
}
}
}
📌 Why? Improves performance and avoids governor limits.
5. Prevent Recursive Trigger Execution
Use a Static Boolean Variable or Set<Id> to prevent recursion.
Example:
public class AccountTriggerHandler {
private static Boolean isTriggerExecuted = false;
public void beforeInsert(List<Account> newAccounts) {
if (isTriggerExecuted) return;
isTriggerExecuted = true;
// Logic here
}
}
📌 Why? Prevents infinite loops when updates trigger another update.
6. Implement Custom Metadata for Configurable Logic
Use Custom Metadata or Custom Settings to control behavior dynamically.
📌 Why? Allows turning features on/off without code changes.
7. Write Unit Tests for the Handler Class
Test each handler method independently.
Use Test.startTest() and Test.stopTest().
Example test:
@isTest
private class AccountTriggerHandlerTest {
@isTest
static void testBeforeInsert() {
List<Account> accounts = new List<Account>{
new Account(Name = 'Test Account')
};
Test.startTest();
insert accounts;
Test.stopTest();
System.assert(accounts[0].Id != null, 'Account should be inserted');
}
}
📌 Why? Ensures trigger logic works correctly and remains bulk-safe.
Final Thoughts
One trigger per object
Use a separate handler class
Bulkify operations
Prevent recursion
Write tests for trigger logic
Following these best practices will make your Salesforce org scalable, efficient, and easy to maintain!
コメント