Skip to main content

Apex Service Layer Design

๐Ÿงฑ Apex Service Layer Design: Helper & Handler Classes

In Apex, the **Service Layer pattern** is used to organize logic in a modular and scalable way. It separates your **trigger logic**, **business logic**, and **utility logic** into focused components:

  • Trigger → Handler Class: Controls the flow based on the context.
  • Handler → Helper Class: Performs the actual business logic.

๐Ÿ“ Why Use Service Layer?

  • ๐Ÿ”„ Makes code reusable and easier to test
  • ๐Ÿงช Simplifies unit testing by separating concerns
  • ⚙️ Prevents logic inside triggers directly (recommended best practice)
---

๐Ÿ“ Folder Structure (Conceptual)

/triggers
  AccountTrigger.trigger

/classes
  AccountTriggerHandler.cls
  AccountHelper.cls
---

1️⃣ Trigger File

This is just a delegator, calling the handler class.

AccountTrigger.trigger

trigger AccountTrigger on Account (before insert, after update) {
    AccountTriggerHandler.handle(Trigger.isBefore, Trigger.isAfter);
}
  
---

2️⃣ Trigger Handler Class

This class controls which logic to call based on the trigger context.

AccountTriggerHandler.cls

public class AccountTriggerHandler {
    public static void handle(Boolean isBefore, Boolean isAfter) {
        if (isBefore && Trigger.isInsert) {
            AccountHelper.setDefaultRating(Trigger.new);
        }
        if (isAfter && Trigger.isUpdate) {
            AccountHelper.logChanges(Trigger.oldMap, Trigger.newMap);
        }
    }
}
  
---

3️⃣ Helper Class

Contains the actual logic. You can reuse these methods anywhere.

AccountHelper.cls

public class AccountHelper {

    public static void setDefaultRating(List accList) {
        for (Account acc : accList) {
            if (String.isBlank(acc.Rating)) {
                acc.Rating = 'Hot';
            }
        }
    }

    public static void logChanges(Map oldMap, Map newMap) {
        for (Id accId : newMap.keySet()) {
            Account oldAcc = oldMap.get(accId);
            Account newAcc = newMap.get(accId);
            if (oldAcc.Name != newAcc.Name) {
                System.debug('Account Name changed from ' + oldAcc.Name + ' to ' + newAcc.Name);
            }
        }
    }
}
  
---

๐Ÿ“Š Handler vs Helper

Component Responsibility Example Method
Trigger Handler Controls logic flow (like a traffic cop) handle(Boolean isBefore, Boolean isAfter)
Helper Class Contains business logic methods setDefaultRating(), logChanges()
---

✅ Best Practices

  • Keep triggers logic-free
  • Write unit tests for helper methods directly
  • Follow naming conventions like ObjectNameTriggerHandler
  • Make helper methods public static for reuse