Intermediate 30 min read

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.

APEX

Add Service Classes Quiz

1. What does extracting AccountRollupService add beyond Module 24's handler-class pattern alone?

Log in to submit the quiz and save your score.

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.