๐งฑ 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