A Guide to Writing Cleaner, Modular, and Maintainable Apex Code in Salesforce
In the world of Salesforce development, writing code that works is only half the battle — writing code that is clean, modular, and maintainable is what separates a good developer from a great one.
As your Salesforce org grows, you’ll find that organizing your Apex code into logical layers makes it easier to maintain, extend, and debug. Three key patterns that help achieve this are:
✅ Service Classes
✅ Selector Classes
✅ Constant Classes
Let’s explore how these layers work together to create a robust and scalable architecture.
1. The Need for Modular Apex Code
When we start writing Apex, it’s easy to put all logic inside triggers or controllers. But over time, this leads to:
-
Repeated code across different triggers or classes.
-
Hard-to-read methods that handle too many things.
-
Difficulty in testing and debugging.
A modular approach solves these issues by separating logic into smaller, focused layers — each with a single responsibility.
That’s where the Service, Selector, and Constant classes come in.
2. The Service Class — The “Brain” of the Logic
The Service class acts as the controller or manager of your business logic.
It contains methods that perform actual operations like creating, updating, or processing records.
Think of it as the “brains” behind the feature — it coordinates logic, calls selectors for data, and uses constants for configuration.
🔹 Example:
Here, the Service class does not directly query data — it delegates that to the Selector class.
This ensures that each layer has a single, clear responsibility.
3. The Selector Class — The “Data Access Layer”
The Selector class is responsible for fetching data from Salesforce.
It contains all your SOQL queries in one place, making it easier to maintain and reuse.
This is often referred to as the data access layer, similar to a “repository” in other programming languages.
🔹 Example:
This way, if you ever need to change your SOQL (for example, add more fields or filters), you do it in one place — the Selector class — without touching your business logic.
4. The Constant Class — The “Single Source of Truth”
In every project, you’ll have repeated values like record type names, queue names, status values, etc.
Instead of hardcoding them across multiple classes, you can define them once in a Constant class and reuse them everywhere.
This helps avoid typos, improves readability, and makes updates effortless.
🔹 Example:
Now, when your service class needs these values, it simply refers to them like this:
If something changes — say the record type name — you update it once in the constant class, and it reflects everywhere.
5. How These Layers Work Together
Here’s how these three layers typically interact:
| Layer | Purpose | Example |
|---|---|---|
| Service Class | Handles business logic and coordination | CaseService.handleCaseCreation() |
| Selector Class | Fetches and returns data | CaseSelector.getAccounts() |
| Constant Class | Stores reusable values | CaseConstants.RECORDTYPE_CX_OPS |
When you combine them:
-
Your code becomes reusable across triggers, controllers, and flows.
-
You avoid hardcoding and repetitive logic.
-
You reduce technical debt and improve code readability.
6. Benefits of This Modular Approach
✅ Clean Separation of Logic — Each layer has its own role, making your code easier to understand.
✅ Improved Testability — You can write unit tests for each layer independently.
✅ Scalability — Easier to add new record types or features without breaking existing logic.
✅ Reusability — Shared logic (like selectors or constants) can be used across multiple service classes.
✅ Ease of Maintenance — Change in one layer doesn’t affect others.
7. Best Practices to Follow
-
Always keep SOQL queries in Selector classes.
-
Avoid business logic in triggers — call Service methods instead.
-
Keep constant values in a single Constants class.
-
Follow naming conventions, e.g.,
CaseService,CaseSelector,CaseConstants. -
Write unit tests for each layer separately.
Conclusion
Writing modular, clean, and maintainable Apex code isn’t just about good habits — it’s about building a foundation for long-term success.
By structuring your Salesforce code into Service, Selector, and Constant layers, you make it organized, scalable, and easy to maintain — ensuring your org remains efficient as it grows.

