Apex Trigger Frameworks — Which pattern should you actually use?

Apex Trigger Frameworks — Which pattern should you actually use?

Raw triggers work on day one. They break on day ninety. Here's a practitioner's guide to the three frameworks you'll encounter in the wild — and exactly when to reach for each one.

The Problem

Why trigger architecture matters more than the trigger itself

Every Salesforce developer has been here: you inherit an org with four triggers on the Account object, zero order of execution control, recursive loops buried in helper classes, and a Tuesday deployment that broke the London team's pipeline for six hours.

The code in each trigger? Fine. The architecture holding them together? Non-existent.

The real cost of "just write a trigger": Salesforce doesn't guarantee execution order across multiple triggers on the same object. Without a framework, you're one update statement away from an infinite loop — or worse, silent data corruption.

Let's break down the three patterns you'll encounter — when each is appropriate, what the code actually looks like, and the decision matrix to make the call fast.


The Patterns

Three frameworks, three different use cases

Pattern 01 (AVOID IN PROD) - Raw Trigger (No Framework)

Logic lives directly in the trigger body. Fine for prototypes, sandboxes, or a quick proof-of-concept. In production, it becomes unmaintainable within weeks — no separation of concerns, impossible to unit test cleanly, and guaranteed to conflict with the next trigger someone adds. #AdminScripting #ScratchOrgsOnly

Pattern 02 (STANDARD CHOICE) - Trigger Handler Pattern

One trigger per object. All logic delegated to a handler class. This is the industry-standard baseline — used in 80%+ of well-maintained orgs. Gives you clean separation, testability, and prevents the multi-trigger chaos. Kevin O'Hara's open-source framework is the canonical implementation. #Developers #SolutionsArchitects

Pattern 03 (ENTERPRISE ONLY) - Enterprise Framework (TDTM / Custom)

Trigger Dispatch with Table-Driven Trigger Management. Logic is registered in custom metadata or custom objects — triggers are fully data-driven and configurable without code deployments. Used in NPSP, Healthcare Cloud, and large ISV packages. Significant overhead to set up; overkill for most implementations. #TechArhitects #ISVBuilders


Code walkthrough

What each pattern looks like in practice

Pattern 01 · AccountTrigger.trigger — Raw trigger (what NOT to do)

trigger AccountTrigger on Account (before insert, before update) {
  // Logic directly in trigger — no separation of concerns
  if (Trigger.isBefore && Trigger.isInsert) {
    for (Account acc : Trigger.new) {
      // Business logic mixed with trigger context
      if (acc.Industry == 'Technology') {
        acc.Rating = 'Hot';
      }
    }
  }
  // Imagine 3 more developers adding blocks here over 6 months...
}        

Pattern 02 · AccountTrigger.trigger — Thin trigger

trigger AccountTrigger on Account (
  before insert, before update, before delete,
  after insert, after update, after delete, after undelete
) {
  // That's it. One line. The handler does everything.
  AccountTriggerHandler.run();
}        

Pattern 02 · AccountTriggerHandler.cls — Handler class

public class AccountTriggerHandler extends TriggerHandler {

  private List<Account> newAccounts;
  private Map<Id, Account> oldAccountMap;

  public AccountTriggerHandler() {
    this.newAccounts = (List<Account>) Trigger.new;
    this.oldAccountMap = (Map<Id, Account>) Trigger.oldMap;
  }

  @Override
  protected void beforeInsert() {
    AccountService.setDefaultRating(this.newAccounts);
  }

  @Override
  protected void afterUpdate() {
    AccountService.syncRelatedContacts(
      this.newAccounts, this.oldAccountMap
    );
  }
}        

Pattern 02 · Recursion guard — prevents infinite loops

public class AccountTriggerHandler extends TriggerHandler {

  // Static flag: survives across trigger re-entries in same transaction
  private static Boolean hasRun = false;

  @Override
  protected void afterUpdate() {
    if (hasRun) return;
    hasRun = true;

    // Safe to update records here — won't re-fire infinitely
    AccountService.syncRelatedContacts(Trigger.new, Trigger.oldMap);
  }
}        
Pro tip: Kevin O'Hara's TriggerHandler base class (free on GitHub) gives you built-in bypass logic, max loop count, and context detection. Use it as your base class rather than building from scratch. It's battle-tested across thousands of orgs.

Decision matrix

Pick your pattern in 30 seconds

Article content

Key takeaways

What to walk away with

  1. One trigger per object — always. Salesforce has no guaranteed execution order across multiple triggers. One trigger + one handler = full control.
  2. The handler does the heavy lifting. Keep triggers thin (one line is ideal). All business logic lives in handler and service classes — testable, modular, reusable.
  3. Always implement a recursion guard. Any DML inside an after trigger can re-fire the same trigger. A static boolean flag or loop count check prevents the infinite loop scenario that kills deployments.
  4. Use Kevin O'Hara's base class. Don't build a handler framework from scratch. The open-source TriggerHandler class is the community standard — well-documented, handles bypass logic, and your team already knows it.
  5. TDTM is for packages, not projects. Unless you're building an ISV product or working in NPSP, the overhead of table-driven trigger management isn't worth it. Use Pattern 02 and move on.

Resources from this edition: github.com/kevinohara80/sfdc-trigger-framework — Kevin O'Hara's TriggerHandler base classSalesforce Docs: Trigger Best Practices → Apex Developer Guide → Triggers and Order of Execution

Playbook Thoughts

Most Salesforce orgs don't have a trigger problem.They have a trigger architecture problem. Identify issues in your current org and fix it.

Salesforce works best when decisions are intentional and execution is grounded in real experience. Each edition of Salesforce Playbooks shares practical patterns, honest insights, and lessons from the field—helping teams design Salesforce orgs that scale without unnecessary complexity.

Follow Salesforce Playbooks to stay ahead with insights you can apply immediately in your Salesforce org.

📩 connect@perigeon.com 🌐 https://www.perigeon.com

© Perigeon Software. All rights reserved.


To view or add a comment, sign in

More articles by Perigeon Software

Others also viewed

Explore content categories