Add Service Classes
By the end of this lesson, you'll be able to:
- Extract the Account rollup logic into a dedicated service class
- Explain why this separation helps beyond just the trigger context
- Call the service class from the trigger handler
Prerequisites: "Handle Governor Limits"
Why go further than the handler class
Module 24's handler-class pattern already separates the trigger from its logic — but the rollup logic itself (applyAdjustments) is currently only reachable through OpportunityTriggerHandler. If a future data-migration script needed to recalculate every Account's Total_Won_Revenue__c from scratch, it couldn't reuse this logic without going through a fake trigger context.
Extracting an AccountRollupService
public class AccountRollupService {
public void adjustWonRevenue(Map<Id, Decimal> amountAdjustmentByAccountId) {
if (amountAdjustmentByAccountId.isEmpty()) {
return;
}
List<Account> accounts = [
SELECT Id, Total_Won_Revenue__c
FROM Account
WHERE Id IN :amountAdjustmentByAccountId.keySet()
];
for (Account acc : accounts) {
Decimal current = acc.Total_Won_Revenue__c == null ? 0 : acc.Total_Won_Revenue__c;
acc.Total_Won_Revenue__c = current + amountAdjustmentByAccountId.get(acc.Id);
}
update accounts;
}
}
Same logic as the previous applyAdjustments method, now living in its own class with a clear, reusable name — AccountRollupService.adjustWonRevenue() can be called from the trigger handler, a batch job, or a one-off Execute Anonymous script equally easily.
The trigger handler now delegates
public class OpportunityTriggerHandler {
public void afterUpdate(List<Opportunity> newOpportunities, Map<Id, Opportunity> oldMap) {
Map<Id, Decimal> amountAdjustmentByAccountId = new Map<Id, Decimal>();
for (Opportunity opp : newOpportunities) {
// ... build amountAdjustmentByAccountId exactly as before ...
}
new AccountRollupService().adjustWonRevenue(amountAdjustmentByAccountId);
}
}
OpportunityTriggerHandler now has one job — detecting which Opportunities changed and by how much — while AccountRollupService has a different, focused job: actually applying an adjustment to Accounts. This is Module 14's Single Responsibility Principle, now spanning two classes instead of living inside one growing handler.
Exercise
As a comment, name one other caller (besides OpportunityTriggerHandler) that could now reuse AccountRollupService.adjustWonRevenue() directly.
Show hint
Think about data cleanup, migrations, or scheduled recalculations.
Add Service Classes Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
This lesson extracts the rollup logic out of the trigger handler entirely, into an AccountRollupService — reusable from anywhere, not just this one trigger.