Why Use a Trigger Framework?
As an org accumulates more objects with trigger logic, teams typically converge on a shared trigger framework - a reusable base handler class that every object-specific handler extends - to avoid re-solving the same problems repeatedly: consistent routing to the correct context method, built-in recursion prevention, and a uniform way to bypass triggers during data migrations or specific automated processes like a mass data-loader run. Without a framework, each new object's trigger tends to reinvent this scaffolding slightly differently, making the codebase harder to maintain as the number of objects grows.
Cricket analogy: A trigger framework is like a franchise's standardized batting order template that every team in a league like the IPL builds new lineups from, instead of each captain inventing a totally different batting philosophy from scratch every match.
Core Components of a Handler Framework
A typical framework centers on an abstract or virtual base class, often literally named TriggerHandler, that exposes overridable methods like beforeInsert(), afterInsert(), beforeUpdate(), and afterUpdate(), plus a run() or dispatch() method that inspects Trigger.isBefore/isAfter and the relevant event flag to call the correct overridden method automatically. Object-specific handlers, such as AccountTriggerHandler, then extend this base class and override only the specific context methods they actually need, leaving the framework to handle routing, bulk-safety checks, and recursion prevention centrally.
Cricket analogy: A base handler class with overridable methods is like a national cricket board's standard fielding-drill template that every state team's coach can override with sport-specific variations while the core drill structure stays identical across all teams.
public virtual class TriggerHandler {
public void run() {
if (Trigger.isBefore) {
if (Trigger.isInsert) beforeInsert();
if (Trigger.isUpdate) beforeUpdate();
} else if (Trigger.isAfter) {
if (Trigger.isInsert) afterInsert();
if (Trigger.isUpdate) afterUpdate();
}
}
protected virtual void beforeInsert() {}
protected virtual void beforeUpdate() {}
protected virtual void afterInsert() {}
protected virtual void afterUpdate() {}
}
public class AccountTriggerHandler extends TriggerHandler {
protected override void afterInsert() {
// Account-specific logic only
}
}Popular Frameworks: Kevin O'Hara Pattern and fflib
Kevin O'Hara's lightweight TriggerHandler pattern, widely adopted because it's a single Apex class with no external dependencies, focuses purely on context dispatch and simple recursion control via a max-loop-count guard. In contrast, the fflib Apex Enterprise Patterns framework layers triggers on top of a full Domain/Selector/Service architecture, where trigger logic lives in domain classes and is far more testable and decoupled, at the cost of a steeper learning curve and more boilerplate for smaller orgs.
Cricket analogy: The lightweight TriggerHandler pattern is like a club-level cricket team using a simple, well-worn batting order sheet, while the fflib framework is like an international team's fully staffed analytics department with dedicated roles for every phase of the game.
Choosing between a lightweight pattern like Kevin O'Hara's TriggerHandler and a full enterprise framework like fflib is largely a function of org complexity: a small org with a handful of custom objects rarely needs fflib's Domain/Selector/Service layering, while a large enterprise org managing dozens of interconnected objects benefits from its enforced separation of concerns.
Bypass Mechanisms and Metadata-Driven Control
Mature trigger frameworks expose a bypass or disable mechanism, commonly backed by a Custom Metadata Type record like Trigger_Setting__mdt with a boolean IsActive__c field per object, so an admin or a data-migration script can disable a specific object's trigger without deploying new code, simply by editing the metadata record in Setup or via the Metadata API. This is especially valuable during large one-time data migrations where re-running validation or automation logic on every migrated record would be both unnecessary and dramatically slower.
Cricket analogy: A bypass mechanism disabling a trigger during migration is like a ground curator temporarily switching off the automatic sprinkler system while resurfacing the pitch, then remembering to switch it back on before the next match or risk a dry, cracked wicket.
Disabling a trigger via a bypass mechanism during a data migration is powerful but dangerous if left on afterward - always pair a bypass with an explicit re-enable step in the same migration script or a scheduled follow-up check, since a silently disabled trigger means all future business logic, like validation or rollups, simply stops firing without any error being thrown.
- Trigger frameworks provide a reusable base handler class so every object's handler shares consistent context routing and recursion control.
- A base class typically exposes overridable methods per context (beforeInsert, afterInsert, etc.) with a central dispatch/run method.
- Kevin O'Hara's lightweight TriggerHandler pattern is a single dependency-free class suited to small and mid-sized orgs.
- fflib Apex Enterprise Patterns layers triggers onto a full Domain/Selector/Service architecture for large, complex orgs.
- A Custom Metadata Type-backed bypass mechanism lets admins disable a specific object's trigger without a code deployment.
- Bypass mechanisms are essential during large data migrations to avoid unnecessary validation/automation overhead on every migrated record.
- Always pair a trigger bypass with an explicit re-enable step to avoid silently disabling business logic long-term.
Practice what you learned
1. What is the main benefit of adopting a shared trigger framework across an org's objects?
2. What distinguishes Kevin O'Hara's TriggerHandler pattern from fflib Apex Enterprise Patterns?
3. How is a modern trigger bypass mechanism typically implemented so it doesn't require a deployment to toggle?
4. Why must a trigger bypass used during a data migration always be paired with a re-enable step?
5. What role do domain classes play in the fflib Apex Enterprise Patterns approach to triggers?
Was this page helpful?
You May Also Like
Apex Triggers Basics
An introduction to what Apex triggers are, how their syntax and events work, and when to reach for a trigger instead of declarative automation.
Bulkification and Best Practices
How to write Apex trigger logic that stays safely within governor limits regardless of whether it processes one record or two hundred.
Trigger Context Variables
A guide to Trigger.new, Trigger.old, their map equivalents, and the boolean context flags that let a single handler branch correctly across all seven trigger contexts.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics