Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
Salesforce Developer
medium

How do you avoid "soup" in a growing Apex codebase — what design patterns help?

Without discipline, an Apex codebase devolves into giant classes with mixed concerns. Key patterns:

1. Single Responsibility Principle. Each class does one thing. AccountTriggerHandler handles trigger orchestration; AccountService handles business logic; AccountRepository handles SOQL/DML.

2. Layered architecture.

  • Trigger layer — minimal; just delegates to handler.
  • Handler layer — orchestrates the trigger context's events.
  • Service layer — business rules. Stateless. Takes inputs, returns outputs.
  • Repository / Selector layer — encapsulates SOQL/DML. Returns sObjects.
  • Domain layer (optional) — wraps sObjects with domain logic (e.g., class AccountDomain { calculateTier(); }).

3. Dependency injection.

apex public class OrderService { private NotificationService notifier; public OrderService(NotificationService notifier) { this.notifier = notifier; } }

Inject collaborators rather than hard-coding new NotificationService(). Enables mocking in tests.

4. Interfaces for testability.

`apex public interface ICalloutService { HttpResponse send(HttpRequest req); }

public class HttpCalloutService implements ICalloutService { ... } public class MockCalloutService implements ICalloutService { ... } // for tests `

5. Selector pattern (FFLib). A class per object that owns SOQL.

apex public class AccountSelector { public List<Account> selectByIds(Set<Id> ids) { return [SELECT Id, Name, Phone FROM Account WHERE Id IN :ids]; } public List<Account> selectActiveByOwner(Id ownerId) { return [SELECT Id FROM Account WHERE OwnerId = :ownerId AND Active__c = true]; } }

Centralises queries; one place to update when schema changes.

6. Trigger handler framework. One trigger per object delegating to a handler class. Avoid logic in trigger files.

7. Custom exceptions for business errors.

apex public class AccountNotFoundException extends Exception {}

Clear semantics; catchable distinct from system exceptions.

8. Avoid static state. Static variables shared across requests cause unpredictable bugs in multi-threaded contexts (Apex is single-threaded but the principle holds).

9. Recognised frameworks:

  • FFLib (Force-DI, Apex Common) — open-source patterns library that codifies the above.
  • Apex Trigger Handler Framework by Kevin O'Hara — popular trigger framework.

10. Code reviews and conventions. Establish team conventions: naming, file structure, dependency direction. Lint with PMD.

A clean Apex codebase reads like layered application code, not glue scripts.

Why this answer works

Senior architecture. Naming FFLib and the layered architecture are senior signals.

Follow-ups to expect

Related dictionary terms