Separating Business Logic from Data Access
By the end of this lesson, you'll be able to:
- Explain why mixing SOQL and business logic in the same method makes testing harder
- Refactor a method to separate data access from logic
Prerequisites: The Selector Layer Pattern
The problem with mixing concerns
A method that queries Accounts and decides which ones are "at risk" and updates them is doing three different jobs at once — hard to unit test the "at risk" decision logic in isolation, since it's welded to a specific query and a specific DML statement.
Separating the concerns
Split into a selector (fetch the data), a pure logic method (decide, given data, what the outcome should be — no SOQL/DML at all), and a service (orchestrate: call the selector, call the logic, perform DML). The pure logic method becomes trivial to test with plain in-memory objects, no database needed.
Pure logic, separated from data access
public with sharing class AccountRiskService {
public static Boolean isAtRisk(Account acc) {
// Pure logic — no SOQL, no DML, easy to test with any Account instance
return acc.AnnualRevenue < 10000 && acc.Industry == 'Retail';
}
public static void flagAtRiskAccounts(List<Account> accounts) {
for (Account acc : accounts) {
if (isAtRisk(acc)) {
acc.Status__c = 'At Risk';
}
}
update accounts;
}
}
isAtRisk() can be tested with a plain new Account(...) in memory — no query needed — because it never touches the database itself.
Exercise
Extract the pure decision logic from this method into its own testable method: a Contact is a 'VIP' if TotalPurchases__c > 5000.
Show hint
The new method should take an sObject and return a Boolean, with no SOQL or DML inside it.
Separating Business Logic from Data Access — Quick Check
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
When a single method both queries data and applies business rules, testing the logic means always paying the cost of a real query too — separating data access (selector) from logic (service) makes each piece easier to read, test, and reuse independently.