Advertisement

Leaderboard — 728×90 desktop / 320×50 mobile
Apex

Apex Trigger Frameworks Explained — Patterns, Best Practices and Implementation

13 June 2025 · 10 min read · Intermediate

A trigger framework is the difference between Apex automation that is maintainable, testable and extensible versus a collection of ad-hoc trigger files that become increasingly dangerous to modify as an org grows.

Why frameworks matter

Without a framework, triggers accumulate organically. Developer A writes one trigger for field defaulting. Developer B writes another for notifications. Developer C writes a third for integration callouts. All three are on the same object. Salesforce executes them in an unpredictable order. One trigger’s DML causes another to fire. A bug in one trigger affects all three. Testing any of them requires testing all of them.

A trigger framework imposes structure that prevents this entropy.

The minimal framework pattern

The minimal implementation has three components: one trigger per object (the dispatcher), a handler class (the logic), and a recursion guard.


// AccountTrigger.trigger — the dispatcher (nothing else here)
trigger AccountTrigger on Account (
before insert, before update, before delete,
after insert,  after update,  after delete, after undelete
) {
AccountTriggerHandler handler = new AccountTriggerHandler();
handler.dispatch();
}

```apex

// AccountTriggerHandler.cls — all logic lives here
public with sharing class AccountTriggerHandler {
    // Recursion guard
private static Boolean isRunning = false;
public void dispatch() {
    if (isRunning) return;
    isRunning = true;
    try {
        if (Trigger.isBefore) {
            if (Trigger.isInsert) onBeforeInsert(Trigger.new);
            if (Trigger.isUpdate) onBeforeUpdate(Trigger.new, Trigger.oldMap);
        }
        if (Trigger.isAfter) {
            if (Trigger.isInsert) onAfterInsert(Trigger.new, Trigger.newMap);
            if (Trigger.isUpdate) onAfterUpdate(Trigger.new, Trigger.newMap, Trigger.oldMap);
        }
    } finally {
        isRunning = false;
    }
}

private void onBeforeInsert(List<Account> newAccounts) {
    // Default field values, set computed fields
    AccountService.defaultFields(newAccounts);
}

private void onAfterInsert(List<Account> newAccounts, Map<Id, Account> newMap) {
    // Create related records, send notifications
    AccountService.createDefaultContacts(newAccounts);
}

private void onBeforeUpdate(List<Account> newAccounts, Map<Id, Account> oldMap) {
    AccountService.handleStatusChange(newAccounts, oldMap);
}

private void onAfterUpdate(
    List<Account> newAccounts,
    Map<Id, Account> newMap,
    Map<Id, Account> oldMap
) {
    AccountService.syncRelatedOpportunities(newAccounts, oldMap);
}

## The Service class pattern

The handler dispatches to service classes that each own a specific domain
of business logic. This keeps the handler as a routing layer and the
service classes as the testable units of logic.

```apex

public with sharing class AccountService {
public static void defaultFields(List<Account> accounts) {
for (Account acc : accounts) {
if (acc.Rating == null) acc.Rating = 'Warm';
}
}

public static void handleStatusChange(
    List<Account> accounts,
    Map<Id, Account> oldMap
) {
    List<Account> changed = new List<Account>();
    for (Account acc : accounts) {
        if (acc.AccountSource != oldMap.get(acc.Id).AccountSource) {
            changed.add(acc);
        }
    }
    if (!changed.isEmpty()) {
        // Handle the subset of changed records
    }
}

}

## The bypass mechanism

Every framework should include a way to disable trigger execution for
specific contexts — data migrations, bulk loads, or emergency maintenance.

```apex

public with sharing class TriggerBypass {
public static Boolean isBypassed(String handlerName) {
// Query Custom Metadata: Trigger_Bypass__mdt
// Return true if bypass is active for this handler
return Trigger_Bypass__mdt.getInstance(handlerName)?.Active__c == true;
}
}

This allows an admin to activate a Custom Metadata bypass record without
deploying code — zero-downtime trigger disabling.

Test your knowledge — Apex

10 questions · Basic to Advanced

0 / 10 correct

Advertisement

In-content — 300×250