Build the Service Layer
By the end of this lesson, you'll be able to:
- Implement each health metric as its own focused method
- Combine them into a single wrapper class for the dashboard
- Keep the service layer entirely free of UI concerns
Prerequisites: "Design the Data Aggregation"
Each metric as its own method
public class AccountHealthService {
public Integer getOpenCaseCount(Id accountId) {
return [SELECT COUNT() FROM Case WHERE AccountId = :accountId AND IsClosed = false];
}
public Decimal getAverageResolutionDays(Id accountId) {
List<Case> closedCases = [
SELECT CreatedDate, ClosedDate FROM Case
WHERE AccountId = :accountId AND IsClosed = true AND ClosedDate != null
];
if (closedCases.isEmpty()) {
return 0;
}
Integer totalDays = 0;
for (Case c : closedCases) {
totalDays += c.CreatedDate.date().daysBetween(c.ClosedDate.date());
}
return totalDays / (Decimal) closedCases.size();
}
}
getOpenCaseCount uses Module 20's COUNT() directly; getAverageResolutionDays computes the average in Apex since it needs a date difference SOQL's AVG() alone can't express — both handle the empty-data case (Module 17's "Handle Edge Cases" habit) up front.
Combining metrics into one wrapper
public class AccountHealthSummary {
public Integer openCaseCount;
public Decimal averageResolutionDays;
public Decimal totalWonRevenue;
public Integer daysSinceLastActivity;
}
public AccountHealthSummary getHealthSummary(Id accountId) {
AccountHealthSummary summary = new AccountHealthSummary();
summary.openCaseCount = getOpenCaseCount(accountId);
summary.averageResolutionDays = getAverageResolutionDays(accountId);
summary.totalWonRevenue = getTotalWonRevenue(accountId);
summary.daysSinceLastActivity = getDaysSinceLastActivity(accountId);
return summary;
}
This is Module 34's DTO/wrapper-class pattern, used here at the service layer rather than directly in the controller — AccountHealthSummary doesn't yet have any @AuraEnabled annotations, since this class has no idea an LWC will ever use it (that's the controller's job, in a later lesson).
Why the service layer stays UI-agnostic
AccountHealthService and AccountHealthSummary could be called from an LWC controller, a batch job, or a plain Execute Anonymous script equally easily — nothing here mentions @AuraEnabled, cacheable, or anything LWC-specific. This mirrors Module 26's AccountRollupService: the logic that computes something and the logic that exposes it to a UI are deliberately kept as separate concerns.
Exercise
Add a getTotalWonRevenue(Id accountId) method that queries Total_Won_Revenue__c directly from the Account.
Show hint
[SELECT Total_Won_Revenue__c FROM Account WHERE Id = :accountId].Total_Won_Revenue__c
Build the Service Layer 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 implements AccountHealthService — plain Apex, with no knowledge of LWC or @AuraEnabled at all, exactly the separation Module 34's controller lesson will build on top of.