Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/switch/field_switch.php on line 17

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/switch/field_switch.php on line 17

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/media/field_media.php on line 46

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/media/field_media.php on line 46

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/select/field_select.php on line 17

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/select/field_select.php on line 17

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/editor/field_editor.php on line 46

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/editor/field_editor.php on line 46

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/text/field_text.php on line 17

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/text/field_text.php on line 17

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/slider/field_slider.php on line 40

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/slider/field_slider.php on line 40

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/radio/field_radio.php on line 17

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/radio/field_radio.php on line 17

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/info/field_info.php on line 45

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/info/field_info.php on line 45

Deprecated: Optional parameter $field declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/typography/field_typography.php on line 50

Deprecated: Optional parameter $value declared before required parameter $parent is implicitly treated as a required parameter in /home1/oijoiv2f/public_html/wp-content/plugins/apus-framework/libs/redux/ReduxCore/inc/fields/typography/field_typography.php on line 50
Apex Trigger Best Practices and the Trigger Framework - Salesforce Next Gen

Deprecated: trim(): Passing null to parameter #1 ($string) of type string is deprecated in /home1/oijoiv2f/public_html/wp-content/themes/entaro/template-posts/single/inner.php on line 23

Apex Trigger Best Practices and the Trigger Framework

Apex Trigger Best Practices and the Trigger Framework

Apex Trigger Best Practices

In this section we will understand the best practices a user needs to follow to ensure the Trigger runs smoothly throughout its lifetime.
We have always run in the situation where we have begun with a trigger which has a few lines of codes and serves a single purpose. But as the time progress our trigger becomes more and more complex with addition of new business logic and keeps on growing. Soon it becomes difficult/ impossible to manage.
Sooner or later we are going to find our self in a situation where a trigger has become too difficult to maintain and even the slightest change may have a disastrous effect. Therefore I have compiled few best practices for writing Apex Trigger to avoid the above mentioned scenarios and they are mentioned below:

1. One Trigger Per Object

The reason this is on top my best practice list is that, if we make more than one Trigger on an object then we are not able to control the order of execution of these trigger’s i.e. there is no proper flow control. A single Trigger is capable enough of handling all the possible combinations of DML events and Trigger’s context. Namely insert, update delete and undelete (Before and After). So as a best practice we just need to create a single Trigger and it will handle all of the context. For example see the below code:

trigger OptyTrigr on Opportunity (
before delete, after insert, after update, after delete, after undelete, before insert, before update) {
Write your Trigger logic here
}

2. Logic-less Trigger

Next good practice is to make Trigger without covering any business logic inside the trigger itself, make it logic-less piece of code. All we want to do here is to invoke the Trigger on the desired DML event and let it delegate the task to a specific handler class. The main reason behind that is testing a Trigger is not easy if all the business logic is written in the body of the Trigger. If we have written methods inside the body of the trigger, then these methods cannot be exposed for testing purpose and methods of trigger cannot be used anywhere in the org. Therefore stuffing logic inside the body of the Trigger will make a mess one day in future. Therefore we should create a helper class which receives delegation from the Trigger and all the application logic is written there. Below is an example of the handler class.
trigger OpportunityTrigger on Opportunity (after insert) //Trigger
{
OpportunityTriggerHandler.HandleAfterInsert(Trigger.new);
}

public class OpportunityTriggerHandler { // Handler Class
public static void handleAfterInsert(List opps) {— Business Logic—-}
}

3. Handler methods Specific to the Context

One more best practice is to create methods which are specific to the context, i.e. they are invoked based on the DML event. If I am handling more than one DML Event then I would write as many methods in the handler class not the Trigger and I would route the trigger flow to the correct Handler method based on the context variables like Trigger.isAfter or Trigger.isInsert etc.

Now we come to the stage that we already know the best practice to create a Trigger but why we should use a Framework for the same. We are going to find answers to this question below.
Why Use Framework?

Trigger framework simplifies the development of a trigger, but it is not necessary to use framework all the time for the development of the trigger. Framework helps in the following manner:-

1. Helps in following best practices
2. Implementation of new logic and new handler method in an easy manner.
3. Maintenance of code and testing of Trigger is simplified
4. Enforces consistent implementation of the trigger business logic
5. It Implements tool, utilities and abstraction and make the handler logic as lightweight as possible which will help in maintaining the code

Let’s discuss the usability of the framework in detail.

Routing Abstraction

Routing abstraction is a logic in which the flow of the trigger transaction is transferred to a method and this transfer of control is based on the context of trigger. The logic will check in which context the trigger was invoked and it will dispatch the flow to the method responsible for handling the context. To implement this logic we have to make the logic to route the flow but if you use trigger framework then this routing would be handled by the framework.

Recursion Detection and Prevention

Another common mistake a developer makes is letting the trigger run in a recursive manner. Recursion is a very powerful tool, but if appropriate care is not taken then it can result in unexpected outcome. Trigger framework, in this case, acts a watchdog and observes all the trigger execution and can detect recursion and figure out a way to handle the situation in an appropriate way.

Centralize Enablement and disablement of Trigger

Turing on and off a trigger is not an easy task and the traditional method using change sets is time consuming. The ability to disable the trigger at will is a huge advantage and this can be achieved using trigger framework. Trigger framework can be connected to a custom setting in the org and it can control the on and off of the trigger.
Let’s understand by following an Example of Framework provided in salesforce developer guide.
Trigger Framework example

To begin with let’s have a look at the basic Implementation of a Trigger on opportunity object.

//Trigger
trigger OpportunityTrigger on Opportunity (after insert, after update, after delete, after undelete) {
new OpportunityTriggerHandler().run();
}

// The Handler Class
public class OpportunityTriggerHandler extends TriggerHandler {

public OpportunityTriggerHandler() {}

}

You can see that although there are more than on context that are being called in the trigger but we have only one line the trigger’s actual body. The only line of code that we have written in the trigger’s body is the one where we are calling the run method of the trigger handler class, i.e. “new OpportunityTriggerHandler().run();”.

Let’s make this example more practical by adding a scenario and logic to it. But where will we write the logic, if you guessed Helper class, then you are right, we will make use of predefined context handler methods which are overridable. As context method are called automatically based on the DML Event we do not have to worry about calling them.

Ok So let’s assume that we have a requirement where we have to update Amount field to zero when an opportunity is marked Closed/Lost. To implement this logic event we will use before update trigger. So the first thing which we will do is to override the existing before update method in the handler class and write our application logic there, and it will get called automatically by the framework. Write code as shown below.

// The Handler Class
public class OpportunityTriggerHandler extends TriggerHandler {

public OpportunityTriggerHandler() {}
protected void override beforeUpdate() {
setLostOppsToZero();
}

private void setLostOppsToZero(List) {
for(Opportunity o : (List) Trigger.new) {
if(o.StageName == ‘Closed Lost’ && o.Amount > 0) {
o.Amount = 0;
}
}
}
}

Here you can see that not a single line of code was written inside the trigger. The only thing which we did was to override the method beforeUpdate in the handler class. But not only this, we had to do one more task and that was to cast the Trigger.new context variable into the list opportunity. We have to cast the context variable into the list of opportunity because trigger automatically cast these variables to the respective object automatically but when these variables are referred outside of the trigger, then we have to first cast them into the specific object type and then perform the required task, as shown above.

So now we know that whenever there is a change in the requirement then we do not have to make any changes in the trigger body but instead modify the trigger handler class. We can have as many context hander methods as there are the DML Events.

Below is the schematic of the Trigger Framework, it mentions what is offered by the framework and what developer has to provide to it.

Salesforce Interview Questions

Entity Diagram of Trigger Framework Image Source: https://code.google.com/archive/p/apex-trigger-architecture-framework/

We have seen now how to implement the framework but how is the framework doing all this. We just created a Trigger handler class that inherit from a base class TriggerHandler provided by the framework. This class has the following roles.

1. It has the definition of the overridable context methods for each event
2. It supervises and manages execution of the Trigger
3. It also provides the routing logic to call the right context method

The run() Method

Run method is the method which provides access to the trigger framework. It initiates when it is called from inside the trigger. It is the method which runs the supervisory logic and is responsible for the appropriate routing of the trigger flow. It does all this by checking the current context and then calls the appropriate context method. Below is the source code of the run method.

public void run() {

if(!validateRun()) return;

addToLoopCount();

// dispatch to the correct handler method
if(Trigger.isBefore && Trigger.isInsert) {
this.beforeInsert();
} else if(Trigger.isBefore && Trigger.isUpdate) {
this.beforeUpdate();
} else if(Trigger.isBefore && Trigger.isDelete) {
this.beforeDelete();
} else if(Trigger.isAfter && Trigger.isInsert) {
this.afterInsert();
} else if(Trigger.isAfter && Trigger.isUpdate) {
this.afterUpdate();
} else if(Trigger.isAfter && Trigger.isDelete) {
this.afterDelete();
} else if(Trigger.isAfter && Trigger.isUndelete) {
this.afterUndelete();
}

}

The methods called in the handler are defined logic-less, they are meant to be overridden if nothing overrides it then no action is taken and nothing really happens. They are defined as below:

Protected virtual void afterInsert(){

}

Supervisor Logic

Trigger handler not only route the trigger execution but also performs other tasks as well, like preventing the recursion etc. Trigger framework provides a utility with which we can control the number of executions per trigger invocation. See the example below:

public class OpportunityTriggerHandler extends TriggerHandler {

public OpportunityTriggerHandler() {
this.setMaxLoopCount(1);
}

public override void afterUpdate() {
List opps = [SELECT Id FROM Opportunity WHERE Id IN :Trigger.newMap.keySet()];
update opps; // this will throw after this update
}

}

Trigger framework also provides us with power to bypass invocation of a trigger. Although it is not a good practice to bypass a trigger but in a scenario where the creation of an object ends up creating another object, in this case a trigger may invoke when the second object is inserted. Below is an example of code where the trigger handler is bypassing the invocation of trigger and later on when the task is completed we can clear the bypass logic from the trigger, see the example code below.

public class OpportunityTriggerHandler extends TriggerHandler {

public override void afterUpdate() {
List opps = [SELECT Id, AccountId FROM Opportunity WHERE Id IN :Trigger.newMap.keySet()];

Account acc = [SELECT Id, Name FROM Account WHERE Id = :opps.get(0).AccountId];

Case c = new Case();
c.Subject = ‘My Bypassed Case’;

TriggerHandler.bypass(‘CaseTriggerHandler’);

insert c; // won’t invoke the CaseTriggerHandler

TriggerHandler.clearBypass(‘CaseTriggerHandler’);

c.Subject = ‘No More Bypass’;
update c; // will invoke the CaseTriggerHandler

}

}

Finally on/off switch ability to enable / disable trigger at the production environment. It has always been difficult to enable and disable the Trigger at production, when it is causing production issue. This is achieved by creating a custom setting in salesforce org and this functionality can be extended to Visualforce page and an admin can easily enable / disable a trigger at Production.

Also check other trigger resource:

  1. Design Patterns for Bulkifying an Apex Trigger
  2. Making the Apex trigger a bit more complex!!
  3. Writing a Simple Apex Trigger !!
  4. Your first Salesforce Apex Trigger from scratch!!!
  5. Salesforce Interview Questions on Trigger

References:
Apex Trigger framework : https://github.com/kevinohara80/sfdc-trigger-framework/blob/master/src/classes/TriggerHandler.cls

Entity Diagram of Trigger Framework : https://code.google.com/archive/p/apex-trigger-architecture-framework/

Trigger Pattern for Tidy, Streamlined, Bulkified Triggers : http://developer.force.com/cookbook/recipe/trigger-pattern-for-tidy-streamlined-bulkified-triggers

Sumit Datta

Sumit Datta

I am a 5x Certified Salesforce developer with overall 7 years of IT experience and 5 years of Implementation experience in Salesforce. I am here to share my knowledge and help Beginners in Salesforce to understand the concepts of Apex, Visualforce, Salesforce Lightning and Salesforce Configuration.

9 Comments

  1. Sumitdatta | Pearltrees September 8, 2017 Reply

    […] Time has elapsed You have reached 0 of 0 points, (0) Your result has been entered into leaderboard. Apex Trigger Best Practices and the Trigger Framework – SalesforceNextGen. Apex Trigger Best Practices In this section we will understand the best practices a user needs to […]

  2. […] Apex Trigger Best Practices and the Trigger Framework […]

  3. […] Apex Trigger Best Practices and the Trigger Framework […]

  4. […] Apex Trigger Best Practices and the Trigger Framework […]

  5. […] Apex Trigger Best Practices and the Trigger Framework […]

  6. […] Apex Trigger Best Practices and the Trigger Framework […]

  7. […] Apex Trigger Best Practices and the Trigger Framework […]

  8. […] Apex Trigger Best Practices and the Trigger Framework […]

  9. […] Apex Trigger Best Practices and the Trigger Framework […]

Leave a Comment

Your email address will not be published.