In this blog, we’re going to break down what Apex design patterns are, why they matter, and how you can use them to write better, cleaner, and more scalable code in Salesforce.
Whether you’re new to development or looking to level up your architecture game, this guide will walk you through the most essential design patterns like Singleton and Factory with simple examples and real-world use cases.
What Are Apex Design Patterns?
Apex design patterns are standard ways to solve common problems that developers usually face while writing code in Salesforce. So, instead of creating a new code solution every time, these patterns provide a tried-and-tested structure to follow, to make your code more organised and easier to manage.
For example, if you often find yourself repeating similar logic in different triggers or classes, a design pattern can help you handle that more smartly and efficiently.
While these patterns originally come from general object-oriented programming concepts, they are adapted in Apex to work within Salesforce’s specific environment, such as limiting DML operations and maintaining the multi-tenant architecture of Salesforce.
Advantages of Using Design Patterns in Apex
- Scalability: It makes it easier to add new features for the future and scale your code logic without changing existing functionality.
- Reusability: We can reuse the logic by writing it once and using it across every component.
- Maintainability: It helps to keep code clean and consistent, making it easier for teams to understand and update.
- Testability: It gives a structured design, helping developers to write unit test cases more easily and with better code coverage.
- Governor Limit Awareness: It helps reduce DML and SOQL usage by organising logic more efficiently.
Common Apex Design Patterns
1. Singleton Pattern in Apex Design Pattern
The Singleton Pattern ensures that a class has only one instance during the lifetime of the request and provides a single point of access to that instance.
For instance, in Apex, this pattern is especially useful when:
- You want to load something once (like preloaded values, configuration data, etc.) and reuse it.
- You want to avoid multiple SOQL queries or object creation.
Scenario: Apply Region-Based Discounts on Case Creation
Let’s say the support team handles Cases from customers across different regions like Northeast, Midwest, Southeast, Central etc, or the sales team has Opportunities from all different regions, and the company offers different discount rates based on where the customer is located.
For example:
- NorthEast → 10%
- MidWest → 15%
- SouthEast → 18%
These discount rules are stored in a custom object called Region_Discount__c and these values need to be applied to new records such as Cases, Opportunities, or even custom objects based on a region field.
So, we can create a utility Class RegionDiscountUtility and a method like getDiscountForRegion(String region) to fetch the correct value. However, the limitation arises when multiple components (like triggers, classes, flows, or scheduled jobs) need to access the same discount logic within the same transaction.
For example, consider a scenario where:
- A Case trigger fetches the discount for a region
- An Opportunity trigger in the same transaction does the same
- A flow or controller independently calls a discount utility
If each component performs its own SOQL query or constructs its own logic, it leads to:
- Duplicate data access and unnecessary SOQL queries
- Redundant code scattered across multiple layers
- Increased maintenance overhead and risk of inconsistencies
This is where the Singleton Pattern becomes essential. By implementing a Singleton class (such as RegionDiscountManager), we:
- Load the discount data once per transaction
- Store it in memory using a centralised map
- Expose a shared method (getDiscountForRegion) to all other classes
- Ensure that any part of the system can reuse this data without re-querying
In short, the Singleton Pattern allows you to create transaction-wide, reusable logic that is consistent, efficient, and scalable.
Below is the core Singleton class that loads all region discounts once and exposes a method to retrieve the discount for any region.
Similarly, the Opportunity Trigger applies region discounts during the same transaction when Opportunities are processed.
Benefits of Using the Singleton Pattern in Apex Design Patterns
- Single instance per transaction: Ensures data (like config or reference records) is loaded only once
- Improved performance: Reduces redundant SOQL queries and object creation
- Governor limits safety: Helps stay within SOQL/DML limits in bulk operations
Summary
The Singleton Pattern makes a class to be instantiated only once per transaction. In Apex, it’s ideal for loading data like settings or reference records once and reusing it across triggers, classes, or flows.
2. Factory Pattern in Apex Design Pattern
The Factory Pattern is used when you want to create objects dynamically without knowing their exact class type in advance. Instead of directly using new ClassName() You define a central “factory” method that returns the right object based on input.
Factory Pattern can be achieved by abstraction, where you define a shared interface or abstract class, and multiple implementing classes provide the actual logic. The factory then returns the correct implementation depending on the scenario.
This pattern is especially useful in Apex when:
- You have multiple implementations of the same interface
- You want to create different objects based on input or conditions
- You want to avoid hardcoding logic across different parts of your system
Scenario: Apply Different Discount Strategies Based on Account Type
You want to apply different discount calculation logic based on the Account Type of a customer. For example:
- Customer → Gets a percentage-based discount (e.g., 10% off)
- Partner → Gets a fixed discount (e.g., $500 off)
- Internal → Gets no discount
This discount logic is needed in multiple places: Opportunity creation, Quote generation, etc.
Problem (Without Factory)
If you handle this with if-else
or switch
logic everywhere, your code becomes:
- Repetitive
- Hard to maintain
- Difficult to extend when new Account Types are added
Solution (With Factory)
Using the Factory Pattern, you:
- Define a common interface like
IDiscountStrategy
i.e. Internal Discount Strategy - Create separate classes like
CustomerDiscount
,PartnerDiscount
,InternalDiscount
to handle specific logic - Use a DiscountFactory utility to return the correct class based on
Account.Type
This way, your business logic stays clean, reusable, and easy to expand.
Step-by-Step Implementation
1. Create Interface – IDiscountStrategy
2. Implementations
CustomerDiscount
– percentage-based
PartnerDiscount
– flat discount
InternalDiscount
– no discount
3. Factory Class – DiscountStrategyFactory
4. Example Usage – In an Opportunity Trigger Handler
Advantages of the Factory Pattern in Apex
- Abstraction: Uses interfaces to separate logic from implementation
- Centralised creation: One place to manage object instantiation
- Scalability: Easily add new logic without modifying existing code
Summary
The Factory Pattern centralises object creation based on input or context, allowing you to return the exact implementation of the interface without hardcoding class names. This is ideal for situations where business logic varies based on conditions (like Account Type), and helps keep your code clean, scalable, and easy to maintain.
Also Read – How to make Callouts in Apex with Example
FAQs
1. What are Apex design patterns?
Apex design patterns are reusable solutions to common problems in Salesforce development. They provide structure to your code, making it more scalable, maintainable, and easier to test.
2. Why should I use design patterns in Salesforce?
Design patterns help you avoid repetitive logic, reduce governor-limit issues, organise code better, and build applications that can grow and evolve over time.
3. When should I use the Singleton Pattern in Apex?
Use a Singleton when you need to load data once (such as settings or reference records) and access it multiple times across triggers, classes, or flows within the same transaction.
4. How is the Factory Pattern different from regular if-else logic?
Factory Pattern removes hardcoded if-else blocks and centralises object creation based on input. It makes it easier to add new logic (like a new discount rule) without modifying existing code.
5. Do design patterns affect governor limits?
Yes, patterns like Singleton help avoid redundant SOQL queries or DML operations, making your code more efficient and bulk-safe.
Conclusion
In this blog, we learned how apex design patterns help you write clean, efficient, and scalable code. Patterns like Singleton and Factory simplify complex logic, reduce duplication, and improve maintainability. By using these patterns thoughtfully, you make your Salesforce applications easier to test, extend, and support now and in the future.