Overview
The Trigger Handler pattern is a design approach used in Salesforce Apex to keep triggers thin and maintainable by delegating processing logic to a separate handler class. It enforces single responsibility, improves testability, and ensures Apex triggers remain bulkified and organized — essential for production-grade Salesforce orgs.
Why use the Trigger Handler pattern?
Using a trigger handler helps you follow Salesforce best practices and keywords like “Trigger Handler pattern”, “Apex triggers”, “bulkification”, “single responsibility principle”, and “trigger frameworks”. Benefits include:
- One trigger per object (delegates work to handler)
- Cleaner, more readable triggers
- Centralized business logic for easier testing and reuse
- Better bulk processing and governor limits control
- Clear separation of trigger-context-specific code from domain/business logic
Core concepts
Typical elements of the Trigger Handler pattern:
- Slim Trigger: The trigger only determines the context (before/after, insert/update/delete) and calls methods on the handler.
- Handler Class: A class (e.g., AccountTriggerHandler) that implements methods like beforeInsert, afterUpdate, beforeDelete, etc.
- Bulkified Methods: Handler methods accept collections (List, Map) rather than single records to support bulk operations.
- Context Guarding: Use Trigger.isBefore, Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete and maps/lists provided by the Trigger object.
- Optional Frameworks: Some teams implement a base TriggerHandler abstract class or a TriggerContext wrapper to standardize behavior (prevent recursion, manage static state, order of operations).
Simple example
Below is a minimal implementation showing a thin trigger that delegates to a handler class.
// AccountTrigger.trigger
trigger AccountTrigger on Account (before insert, before update, after insert, after update, before delete) {
if (Trigger.isBefore) {
if (Trigger.isInsert) AccountTriggerHandler.beforeInsert(Trigger.new);
if (Trigger.isUpdate) AccountTriggerHandler.beforeUpdate(Trigger.new, Trigger.oldMap);
if (Trigger.isDelete) AccountTriggerHandler.beforeDelete(Trigger.oldMap);
}
if (Trigger.isAfter) {
if (Trigger.isInsert) AccountTriggerHandler.afterInsert(Trigger.newMap);
if (Trigger.isUpdate) AccountTriggerHandler.afterUpdate(Trigger.newMap, Trigger.oldMap);
}
}
// AccountTriggerHandler.cls
public with sharing class AccountTriggerHandler {
public static void beforeInsert(List
// perform validations or defaulting - bulkified
for (Account a : newList) {
if (String.isBlank(a.AccountNumber)) {
a.AccountNumber = 'AUTO-' + Datetime.now().getTime();
}
}
}
public static void beforeUpdate(List
// compare old and new values using oldMap - bulkified
}
public static void afterInsert(Map
// perform async work, future/batch/queueable calls
}
public static void afterUpdate(Map
// post-processing
}
public static void beforeDelete(Map
// cleanup or prevent delete logic
}
}
Best practices and patterns
- Use one trigger per SObject and delegate to a handler class for all contexts.
- Keep handler public static entry points that accept collections for bulk safety.
- Separate concerns: validation, record updates, callouts, and complex business logic should be in different private/helper methods or classes.
- Respect governor limits: avoid SOQL/DML inside loops; batch queries and DML operations.
- Prevent recursion: use a static Boolean flag or a more robust TriggerContext manager to avoid infinite loops.
- Write focused unit tests for handler methods and simulate Trigger context using Test methods and Test.startTest()/Test.stopTest().
- Consider using or building a lightweight trigger framework (BaseTriggerHandler) for consistent lifecycle hooks (beforeValidate, beforeSave, afterCommit, etc.).
Common extensions
Advanced teams often extend the pattern with:
- TriggerContext objects that expose common properties (isInsert, isUpdate, newList, oldMap) and helper methods.
- Handler base classes with overridable lifecycle methods so child handlers only implement needed stages.
- Centralized exception handling and custom logger utilities for easier debugging.
Conclusion
The Trigger Handler pattern is an essential architectural practice in Salesforce development. It enforces clean, testable, and bulk-safe triggers by isolating trigger orchestration from the actual business logic. For teams building scalable Salesforce apps, adopting this pattern (or a variant/framework) reduces technical debt and simplifies maintenance.








Leave a Reply