Tuesday, July 11, 2017

Trigger and it's handler class example with design pattern.

Please check my previous post (Trigger and it's handler class structure for your organisation.), where I mentioned the architectural structure to design or handle trigger for your organisation.

For the same you should have the below apex classes and trigger,

1. TriggerHandlerException class [Handled exception].
2. TriggerHandler class [An abstract class where all trigger related operations have been structured as a template].
3. TriggerFactory class [Factory of Concrete Trigger Handler classes].
4. TriggerWorker class [This is the worker class for trigger. Each and every trigger just call "executeTrigger()" method of this worker class].
5. ConcreteTriggerHandler class [This is the main handler class, where the actual logic will be define and this class extend TriggerHandler abstract class and defined all abstract method].
6. Trigger for an SObject [From here only call the "executeTrigger()" method of this TriggerWorker class with string parameter as "ConcreteTriggerHandler class name"].


Suppose we want to create a trigger of Account object, then the source code will be as follows,

1. TriggerHandlerException
/*
 * Author : Arun Kumar Hazra
 * Date : 16-NOV-2017
 *
 * Description : This is custom exception calss and fire while trying to create an
 * handler instance which is not registered.
 *.
 */
public class TriggerHandlerException extends Exception {}

2. TriggerHandler
/*
 * Author : Arun Kumar Hazra
 * Date : 16-NOV-2016
 *
 * Description : This abstract class is responsible to create a template to handle trigger operations.
 *               All Trigger Handlers must implement to enforce best practice.
 */
public abstract class TriggerHandler{
    //Template method
    public void doExecute(){
        // Before Trigger
        if (Trigger.isBefore){
            // Iterate through the records to be deleted passing them to the handler.
            if (Trigger.isDelete)
            {
                beforeDelete(Trigger.old, Trigger.oldMap);
            }
            // Iterate through the records to be inserted passing them to the handler.
            else if (Trigger.isInsert)
            {
                beforeInsert(Trigger.new);
            }
            // Iterate through the records to be updated passing them to the handler.
            else if (Trigger.isUpdate)
            {
                List<SObject> lstOfNewSo = new List<SObject>();
                for (SObject so : Trigger.old)
                {
                    lstOfNewSo.add(Trigger.newMap.get(so.Id));                        
                }
                beforeUpdate(Trigger.old, lstOfNewSo, Trigger.oldMap, Trigger.newMap);
            }

        } else { // After Trigger
            // Iterate through the records deleted passing them to the handler.
            if (Trigger.isDelete)
            {
                afterDelete(Trigger.old, Trigger.oldMap);
            }
            // Iterate through the records inserted passing them to the handler.
            else if (Trigger.isInsert)
            {
                afterInsert(Trigger.new, Trigger.newMap);
            }
            // Iterate through the records updated passing them to the handler.
            else if (Trigger.isUpdate)
            {
                List<SObject> lstOfNewSo = new List<SObject>();
                for (SObject so : Trigger.old)
                {
                    lstOfNewSo.add(Trigger.newMap.get(so.Id));
                }
                afterUpdate(Trigger.old, lstOfNewSo, Trigger.oldMap, Trigger.newMap);
            }
    
        }
    }
    //############################### After operations ###############################
    /*
     * This method is called iteratively for each record inserted during an AFTER trigger.
     */
    
    //@TestVisible
    abstract void afterInsert(List<SObject> lstOfNewSo, map<id,SObject> mapOfNewSo);
    /*
     * This method is called iteratively for each record updated during an AFTER trigger.
     */
    
    
    abstract void afterUpdate(List<SObject> lstOfOldSo, List<SObject> lstOfNewSo, map<id,SObject> mapOfOldSo, map<id,SObject> mapOfNewSo);
    /*
     * This method is called iteratively for each record deleted during an AFTER trigger.
     */
    
    
    abstract void afterDelete(List<SObject> lstOfOldSo, map<id,SObject> mapOfOldSo);
    //############################### Before operations ###############################
    /*
     * This method is called iteratively for each record inserted during an BEFORE trigger.
     */
    
    
    abstract void beforeInsert(List<SObject> lstOfNewSo);
    /*
     * This method is called iteratively for each record updated during an BEFORE trigger.
     */
    
    
    abstract void beforeUpdate(List<SObject> lstOfOldSo, List<SObject> lstOfNewSo, map<id,SObject> mapOfOldSo, map<id,SObject> mapOfNewSo);
    /*
     * This method is called iteratively for each record deleted during an BEFORE trigger.
     */
    
    
    abstract void beforeDelete(List<SObject> lstOfOldSo, map<id,SObject> mapOfOldSo);
}

3. TriggerFactory
/*
 * Author : Arun Kumar Hazra
 * Date : 16-NOV-2016
 *
 * Description : This is a factoy class of all triggers, existes in this ORG. This factory is responsible for
 *               creating factory of trigger handler class.
 */
public with sharing class TriggerFactory{
    /*
     * This method return instance of TriggerHandler.
     * Arguments:   String className - Name of handler class in string format.
     * Return triggerHandler : Instance of trigger handler class.
     *  
     */
    public static TriggerHandler getHandlerInstance(String className){
        TriggerHandler handler = getHandler(className);
        // Make sure we have a handler registered, new handlers must be registered in the getHandler method.
        if (handler == null){
            throw new TriggerHandlerException('No Trigger Handler registered of name : ' + className);
        }        
        return handler;
    }
    
    /*
     * private static method to get the appropriate handler for the object type.
     * Modify this method to add any additional handlers.
     * Arguments:   String className - Name of handler class in string format.
     * Returns:     A trigger handler if one exists or null.
     */
    private static TriggerHandler getHandler(String className){
        if (className != null && className.trim().length() > 0){
            Type t = Type.forName(className);
            return (TriggerHandler) t.newInstance();
        }        
        return null;
    }
}

4. TriggerWorker
/*
 * Author : Arun Kumar Hazra
 * Date : 16-NOV-2016
 *
 * Description : This is the first entry point for each trigger.
 *.
 */
global with sharing class TriggerWorker{
    /*
     * This static method is responsible to execute trigger logic with help of handler class.
     * Arguments:   String className - Name of handler class in string format.
     *  
     */
    global static void executeTrigger(String className){
        TriggerHandler handler = TriggerFactory.getHandlerInstance(className);
        handler.doExecute();
    }
}


5. AccountTriggerHandler  (ConcreteTriggerHandler class)
/**
* Trigger Handler class for Expense Detail Objcet.
*
**/
public class AccountTriggerHandler extends TriggerHandler{
    // ########### Start of defining abstract methods of Trigger Handler abstract class ###########
    public void afterInsert(List<SObject> lstOfNewSo, map<id,SObject> mapOfNewSo){
        sampleMethod(lstOfNewSo);
    }
    
    public void afterUpdate(List<SObject> lstOfOldSo, List<SObject> lstOfNewSo, map<id,SObject> mapOfOldSo, map<id,SObject> mapOfNewSo){
        
    }
    
    public void afterDelete(List<SObject> lstOfOldSo, map<id,SObject> mapOfOldSo){
        
    }
    
    public void beforeInsert(List<SObject> lstOfNewSo){
        
    }
    
    public void beforeUpdate(List<SObject> lstOfOldSo, List<SObject> lstOfNewSo, map<id,SObject> mapOfOldSo, map<id,SObject> mapOfNewSo){
        
    }
    
    public void beforeDelete(List<SObject> lstOfOldSo, map<id,SObject> mapOfOldSo){
        
    }
    // ########### End of defining abstract methods of Trigger Handler abstract class ###########
    
    /* here create private method(s) to perform your operation and call that method(s) 
     * from above template method based on the scenario. Suppose we need perform an 
     * operation after a record insertion, so create a private method say sampleMethod() and 
     * call it from above afterInsert() method. */

   private void sampleMethod(lstOfNewSo){
        //Perform your logic.
   }
       
}

6. AccountTrigger

trigger AccountTrigger on Account(after insert, after update, after delete, before insert, before update, before delete) {
   // Method parameter as concrete trigger handler class name.
   TriggerWorker.executeTrigger('AccountTriggerHandler');
}

Data Cloud Part 6: Practical (Ingest Physical Store's Customer details and Sales details)

Hope you have already created your Data Cloud Org and also downloaded the Customer's details and Sales details from previous posts.  Now...