TrailForceTips

Trigger Handler Pattern in Apex Salesforce

Introduction

Salesforce is a powerful platform for customizing and automating business processes. However, as systems grow, complexity also increases. One of the main challenges faced by Salesforce developers is keeping Apex trigger code organized and easy to maintain. To address this issue, the Trigger Handler pattern is widely used.

In this article, we will explore what the Trigger Handler pattern is, its advantages, and how to implement it with practical examples.

The Problem with Complex Triggers

Triggers in Salesforce are used to execute Apex code in response to events on objects, such as inserting, updating, or deleting records. However, adding more logic to the trigger can make it:

  • Hard to read and understand;
  • Difficult to maintain and test;
  • Prone to governance issues, such as excessive execution of queries or DML statements.

The Trigger Handler pattern was created to solve these problems by separating business logic from the trigger’s main code.

What is the Trigger Handler Pattern?

The Trigger Handler pattern is an approach that moves business logic to a separate class, called a “Handler.” This results in a trigger that is small and clean, delegating most of the logic to helper classes.

Benefits:

  1. Better Organization: Shorter and more modular code.
  2. Easier Maintenance: Modifying or adding functionality becomes simpler.
  3. Testability: Facilitates the creation of unit tests.
  4. Code Reusability: Handler classes can be reused by other triggers or workflows.

Practical Implementation

Let’s implement an example using the Account object. Suppose we have the following requirements:

  1. Update the “Industry” field to “Technology” when “AnnualRevenue” is greater than $1,000,000.
  2. Log entries in a custom object whenever an account is inserted.

Simple Trigger

Before the Trigger Handler pattern, the trigger would look like this:

trigger AccountTrigger on Account (before insert, before update) {
    for (Account acc : Trigger.new) {
        if (acc.AnnualRevenue > 1000000) {
            acc.Industry = 'Technology';
        }
    }

    if (Trigger.isInsert) {
        List<Account_Log__c> logs = new List<Account_Log__c>();
        for (Account acc : Trigger.new) {
            logs.add(new Account_Log__c(Name = 'Account Created: ' + acc.Name));
        }
        insert logs;
    }
}

This code quickly becomes difficult to maintain as new requirements emerge. Let’s refactor it using the Trigger Handler pattern.

Implementing the Trigger Handler Pattern

  1. Trigger
trigger AccountTrigger on Account (before insert, before update) {
    AccountTriggerHandler handler = new AccountTriggerHandler();

    if (Trigger.isBefore) {
        if (Trigger.isInsert || Trigger.isUpdate) {
            handler.handleBeforeInsertOrUpdate(Trigger.new);
        }
    }

    if (Trigger.isAfter) {
        if (Trigger.isInsert) {
            handler.handleAfterInsert(Trigger.new);
        }
    }
}
  1. Handler Class
public class AccountTriggerHandler {

    public void handleBeforeInsertOrUpdate(List<Account> accounts) {
        for (Account acc : accounts) {
            if (acc.AnnualRevenue > 1000000) {
                acc.Industry = 'Technology';
            }
        }
    }

    public void handleAfterInsert(List<Account> accounts) {
        List<Account_Log__c> logs = new List<Account_Log__c>();
        for (Account acc : accounts) {
            logs.add(new Account_Log__c(Name = 'Account Created: ' + acc.Name));
        }
        insert logs;
    }
}

Benefits of Refactoring

  • The trigger is now small and only responsible for delegating logic.
  • The handler class can be tested separately, allowing for greater test coverage.

Best Practices

  1. One Handler Class per Object: This avoids confusion and makes locating logic easier.
  2. Avoid Logic in Triggers: All complex code should reside in the handler class.
  3. Use Context Variables Carefully: Use Trigger.new, Trigger.old, etc., only in the trigger, passing necessary lists to the handler.
  4. Organize by Events: Divide handler logic into methods corresponding to trigger events (e.g., beforeInsert, afterUpdate).
  5. Respect Salesforce Limits: Always optimize for bulk operations and avoid exceeding governor limits.

Conclusion

The Trigger Handler pattern is essential to ensure that triggers in Salesforce are easy to understand, maintain, and scale. It promotes good development practices and helps avoid common issues associated with poorly designed triggers.

By applying this pattern to your projects, you will ensure higher code quality and contribute to the system’s long-term sustainability.