Table of Contents
Introduction
Apex Triggers are pieces of apex code that fire when any kind of DML operation or event occurs (insert, update, delete, undelete). Triggers when not used correctly, can lead to trouble. Triggers can interfere with one another and can cause performance issues.
Challenges with Triggers in Salesforce
1. Difficult to maintain heavy complex implementation:
When we write the complete business logic or entire piece of code within the trigger, the trigger gets complex. So with this, as per the new requirements we get in the future, we will be adding that logic in the trigger which will make our trigger very lengthy, would make it difficult for developers to analyse or read the trigger and would become very hard to maintain and implement it as well.
2. Multiple triggers on the same object for the same event:
Here, if we have multiple triggers written for the same event on the same object, then we can’t determine which of these triggers written on that particular object will execute first since there is no order of execution of triggers due to which users might encounter an unexpected result.
3. Recursive execution of triggers:
Often in an org, multiple automation could run, resulting in the DML operations occurring for the same object. For instance, there is a trigger as well as other automation tools for which logic is written for the same object which is executing whenever the record gets updated. In such a situation, if we don’t have a clear structure to handle this, there could be an infinite loop created due to which we might hit the governor limits with Recursive Triggers.
How Trigger Patterns are used to solve these challenges?
A trigger pattern is a design pattern that is used to solve commonly occurring problems like multiple triggers on the same object, recursive triggers, and SOQL 101 exceptions.
When we follow this pattern, it helps us to implement Apex triggers efficiently such that it helps us to overcome the common Apex implementation problems and these design patterns are oriented towards making sure that the implementation challenges are considered.
Here, we have the Trigger Handler Pattern which aims to break down the entire piece of trigger logically in steps such that each specific piece of code has some responsibility. This would be implemented with the help of the Handler Class and apart from the handler class, we have other Apex classes which are more driven to implement business logic or utility functions.
Using this design pattern makes the implementation easy and clean and makes the code reusable. It allows code to be called from other codes, making the unit testing very much easier due to the clean implementation of code.
What is an Advanced Trigger in Salesforce?
In advanced triggers, we have only one trigger per object, a separate handler class, and other Apex utility classes that contain the business logic. It makes the implementation very easy and efficient and free from recursive triggers in Salesforce.
Example
Trigger:
trigger ConTrigger on Contact(after insert, after update) { switch on Trigger.operationType { when AFTER_INSERT { ContactHandlerClass.afterInsertHandler(Trigger.new); } when AFTER_UPDATE { ContactHandlerClass.afterUpdateHandler(Trigger.new, Trigger.oldMap); } } }
Contact Handler Class:
public with sharing class ContactHandlerClass { public static void afterInsertHandler(List newContacts) { Set accountIds = new Set(); for (Contact con : newContacts) { if (String.isNotBlank(con.AccountId)) { accountIds.add(con.AccountId); } } AccountContactClass.updateActiveContacts(accountIds); } public static void afterUpdateHandler(List newContacts, Map oldContactsMap) { Set accountIds = new Set(); for (Contact cont : newContacts) { // capturing and storing the account id into set only if the value of active checkbox is changed if(String.isNotBlank(cont.AccountId) && oldContactsMap.get(cont.Id).Active__c != cont.Active__c) { // write automation logic here accountIds.add(cont.AccountId); } else if (oldContactsMap.get(cont.Id).AccountId != cont.AccountId) { accountIds.add(cont.AccountId); accountIds.add(oldContactsMap.get(cont.Id).AccountId); } } AccountContactClass.updateActiveContacts(accountIds); } }
Account Contact Class:
public with sharing class AccountContactClass { public static void updateActiveContacts(Set accIds) { // get aggregate result for all accounts List output = [ SELECT AccountId, COUNT(Id) totalNumOfContacts FROM Contact WHERE AccountId IN :accIds AND Active__c = TRUE GROUP BY AccountId ]; // creating a list of accounts to get updated List<Account> updateAccounts = new List<Account>(); for (AggregateResult res : output) { // Obtaining Account Id and total of contacts which are active String actId = String.valueOf(res.get('AccountId')); Integer totalConts = Integer.valueOf(res.get(‘totalNumOfContacts’)); // usage of Id field is mandatory to update it in the Account Account act = new Account(Id = actId, Active_Contacts__c = totalNumOfContacts); updateAccounts.add(acc); } // updating Account List here update updateAccounts; } }
0 Comments