Reducing APEX Transaction Governor Limits with the Singleton Pattern

Author: Bruce Tollefson Published: February 27, 2022; Modified: May 9, 2022
code

There are many best practices to adhere to when writing APEX. One of them around governor limits is to reduce the number of SOQL queries within the APEX code. A very common way to reduce queries and not to hit the dreaded: System.LimitException: Too many SOQL queries: 101 error is to use the singleton pattern. This pattern when ran will only create a single instance during the transaction. With this pattern, if the APEX code calls the instance to be instantiated multiple times it will first check to see if it has already been created. If the class has already been created then it will return the already created instance, if not then it will instantiate the class and return the newly instantiated instance.


What is the benefit of this pattern? The benefit of this pattern is to reduce the governor limits within an APEX transaction along with having to do heavy-cost operations only once. As an example let’s say you needed to check to see if the current user’s profile is a part of a set of profiles. If it is, do action A, if it is not do action B. For this example, you don’t want to hard code a list of profile Ids because you will be deploying this code in multiple environments and you don’t want to keep track of each set of Ids in each environment. You create a custom metadata type to hold the names of the profiles which you would like to check against. If there was only one place in the code that needed to know the information you could put it directly code as a private method like so:

public with sharing class foo { public static void bar(){ if(isApartOfProfileGrouping()){ //execute block A... }else{ //execute block B... } } private static boolean isApartOfProfileGrouping() { Boolean isInProfileGrouping = false;//default to false String userProfileName = [Select Name from Profile where Id = :UserInfo.getProfileId()].Name;//get the current User's profile name Set<String> ProfileNameSet = new Set<String>(); for(Profile_List__mdt profile :Profile_List__mdt.getAll().values()){//loop through the list of Profile MDT and put in String set ProfileNameSet.add(profile.developerName); } if(ProfileNameSet.contains(userProfileName))isInProfileGrouping=true; return isInProfileGrouping; } }

However what if there is a new class or method that needs to do the same check, now you don’t want to have the same code in multiple places so you decide to put the private method into a Utility class?

public with sharing class Utility { public static boolean isApartOfProfileGrouping() { Boolean isInProfileGrouping = false;//default to false String userProfileName = [Select Name from Profile where Id = :UserInfo.getProfileId()].Name;//get the current User's profile name Set<String> ProfileNameSet = new Set<String>(); for(Profile_List__mdt profile :Profile_List__mdt.getAll().values()){//loop through the list of Profile MDT and put in String set ProfileNameSet.add(profile.developerName); } if(ProfileNameSet.contains(userProfileName))isInProfileGrouping=true; return isInProfileGrouping; } }

Classes calling the Utility method:

public with sharing class foo { public static void bar(){ if(Utility.isApartOfProfileGrouping()){ //execute block A... }else{ //execute block B... } } }
public with sharing class foo2 { public static void bar(){ if(Utility.isApartOfProfileGrouping()){ //execute block A... }else{ //execute block B... } } }

Instead of the Utility method using only 1 SOQL query, it is now using 2 SOQL queries as each time it is called it needs to run the code again. This is where the singleton comes into play. Instead of calling the code multiple times, the singleton class will run the code once and if called a second time will return the previous instance. With this pattern, the class can be called as many times as needed without increasing limits each time. The APEX the singleton class would look like so:

public class profileSingleton { // private static variable referencing the class private static profileSingleton instance = null; public Boolean isInProfileGrouping {get;private set;} // checks if the profile is within the metadata grouping // The constructor is private and initializes profile grouping check private profileSingleton(){ isApartOfProfileGrouping(); } private void isApartOfProfileGrouping(){ isInProfileGrouping = false;//default to false String userProfileName = [Select Name from Profile where Id = :UserInfo.getProfileId()].Name;//get the current User's profile name Set<String> ProfileNameSet = new Set<String>(); for(Profile_List__mdt profile :Profile_List__mdt.getAll().values()){//loop through the list of Profile MDT and check if it contains the value if(profile.MasterLabel == userProfileName){ isInProfileGrouping=true; break;//found a match don't need to go through the rest of the loop } } } // a static method that returns the instance of the record type public static profileSingleton getInstance(){ // lazy load the record type - only initialize if it doesn't already exist if(instance == null) instance = new profileSingleton(); return instance; } }

The above creates a singleton class, instantiates the class or returns the current instance, and upon instantiation will fire the logic to value the isInProfileGrouping variable. As we can see the variable can only be set from the singleton class. Now replace the Utility method with the singleton class:

public with sharing class foo { public static void bar(){ profileSingleton pS = profileSingleton.getInstance();//either returns the current instance or creates a new one if(pS.isInProfileGrouping){//if the profileSingleton would not be called anywhere else it could be condensed to profileSingleton.getInstance().isInProfileGrouping //execute block A... }else{ //execute block B... } } }
public with sharing class foo2 { public static void bar(){ if(profileSingleton.getInstance().isInProfileGrouping){ //execute block A... }else{ //execute block B... } } }

*If you would like to you could have changed the utility instead of the 2 classes.
This pattern can become very helpful when needing to reduce APEX transaction limits for a method or set of methods that only need to be called and calculated once. In the above example if you wanted to make it more dynamic instead of returning a boolean the metadata could have multiple categories and return a string with a profile category.
Link to GitHub repo.

Leave a Reply

Your email address will not be published. Required fields are marked *