Sunday, 6 January 2019

centralized trigger framework in Apex

AccountTrigger.Trigger :
========================

trigger AccountTrigger on Account (before insert, before update, before delete, after insert, after update, after delete, after undelete)
{

    // This is the only line of code that is required.

   TriggerFactory.createTriggerDispatcher(Account.sObjectType);

}


TriggerFactory.cls :
=====================

/**
 This factory creates the correct dispatcher and dispatches the trigger event(s) to the appropriate
event handler(s). The dispatchers are automatically created using the Type API, hence dispatcher
registration is not required for each dispatchers.
*/
public with sharing class TriggerFactory
{
    /**
Creates the appropriate dispatcher and dispatches the trigger event to the dispatcher's event handler method.
@param Schema.sObjectType Object type to process (SObject.sObjectType)
*/
    public static void createTriggerDispatcher(Schema.sObjectType soType)
    {
        ITriggerDispatcher dispatcher = getTriggerDispatcher(soType);
       if (dispatcher == null)
            throw new TriggerException('No Trigger dispatcher registered for Object Type: ' + soType);
        execute(dispatcher);
    }

    /**
Dispatches to the dispatcher's event handlers.
@param ITriggerDispatcher A Trigger dispatcher that dispatches to the appropriate handlers
*/
    private static void execute(ITriggerDispatcher dispatcher)
    {
    TriggerParameters tp = new TriggerParameters(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap,
Trigger.isBefore, Trigger.isAfter, Trigger.isDelete,
Trigger.isInsert, Trigger.isUpdate, Trigger.isUnDelete, Trigger.isExecuting);
        // Handle before trigger events
        if (Trigger.isBefore) {
            dispatcher.bulkBefore();
            if (Trigger.isDelete)
                dispatcher.beforeDelete(tp);
            else if (Trigger.isInsert)
                dispatcher.beforeInsert(tp);
            else if (Trigger.isUpdate)
                dispatcher.beforeUpdate(tp);       
        }
        else // Handle after trigger events
        {
            dispatcher.bulkAfter();
            if (Trigger.isDelete)
                dispatcher.afterDelete(tp);
            else if (Trigger.isInsert)
                dispatcher.afterInsert(tp);
            else if (Trigger.isUpdate)
                dispatcher.afterUpdate(tp);
        }
        dispatcher.andFinally();
    }

    /**
Gets the appropriate dispatcher based on the SObject. It constructs the instance of the dispatcher
dynamically using the Type API. The name of the dispatcher has to follow this format:
<ObjectName>TriggerDispatcher. For e.g. for the Feedback__c object, the dispatcher has to be named
as FeedbackTriggerDispatcher.
  @param Schema.sObjectType Object type to create the dispatcher
@return ITriggerDispatcher A trigger dispatcher  if one exists or null.
   */
    private static ITriggerDispatcher getTriggerDispatcher(Schema.sObjectType soType)
    {
    String originalTypeName = soType.getDescribe().getName();
    String dispatcherTypeName = null;
    if (originalTypeName.toLowerCase().endsWith('__c')) {
    Integer index = originalTypeName.toLowerCase().indexOf('__c');
    dispatcherTypeName = originalTypeName.substring(0, index) + 'TriggerDispatcher';
    }
    else
    dispatcherTypeName = originalTypeName + 'TriggerDispatcher';
Type obType = Type.forName(dispatcherTypeName);
ITriggerDispatcher dispatcher = (obType == null) ? null : (ITriggerDispatcher)obType.newInstance();
    return dispatcher;
    }
}


AccountTriggerDispatcher.cls :
===============================

/**
 This class extends the TriggerDispatcherBase to provide the dispatching functionality for the trigger actions
 on the Account object. The event handlers support allowing and preventing actions for reentrant scenarios.
 This is controlled by the flag isBeforeXxxxx and isAfterXxxxx member variables. These variables need to be set
 to true before invoking the handlers and set to false after the invocation of the handlers. Resetting is MUST
 as otherwise unit tests MAY fail. The actual actions should be placed in the handlers (in a separate class).
*/
public class AccountTriggerDispatcher extends TriggerDispatcherBase
{
/** Stores if before insert handler is processing */
private static Boolean isBeforeInsertProcessing = false;
/** Stores if before update handler is processing */
private static Boolean isBeforeUpdateProcessing = false;
/** Stores if before delete handler is processing */
private static Boolean isBeforeDeleteProcessing = false;
/** Stores if after insert handler is processing */
private static Boolean isAfterInsertProcessing = false;
/** Stores if after update handler is processing */
private static Boolean isAfterUpdateProcessing = false;
/** Stores if after delete handler is processing */
private static Boolean isAfterDeleteProcessing = false;
/** Stores if after undelete handler is processing */
private static Boolean isAfterUnDeleteProcessing = false;

/**
Called by the trigger framework to carry out the actions before the records are inserted. If there is an
existing call running on the same context, the rentrant call will utilize the handler that was created
in the original call.
TriggerParameters Contains the trigger parameters which includes the records that is getting inserted.
*/
public virtual override void beforeInsert(TriggerParameters tp) {
if(!isBeforeInsertProcessing) {
isBeforeInsertProcessing = true;
execute(new AccountBeforeInsertTriggerHandler(), tp, TriggerParameters.TriggerEvent.beforeInsert);
isBeforeInsertProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.beforeInsert);
}

/**
Called by the trigger framework to carry out the actions before the records are updated. If there is an
existing call running on the same context, the rentrant call will utilize the handler that was created
in the original call.
TriggerParameters Contains the trigger parameters which includes the records that is getting updated.
*/
public virtual override void beforeUpdate(TriggerParameters tp) {
if(!isBeforeUpdateProcessing) {
isBeforeUpdateProcessing = true;
execute(new AccountBeforeUpdateTriggerHandler(), tp, TriggerParameters.TriggerEvent.beforeUpdate);
isBeforeUpdateProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.beforeUpdate);
}

        public virtual override void beforeDelete(TriggerParameters tp) {
if(!isBeforeDeleteProcessing) {
isBeforeDeleteProcessing = true;
execute(new AccountBeforeDeleteTriggerHandler(), tp, TriggerParameters.TriggerEvent.beforeDelete);
isBeforeDeleteProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.beforeDelete);
}

/**
Called by the trigger framework to carry out the actions after the record(s) are created. If there is an
existing call running on the same context, the rentrant call will utilize the handler that was created
in the original call.
TriggerParameters Contains the trigger parameters which includes the record(s) that got created.
*/
public virtual override void afterInsert(TriggerParameters tp) {
if(!isAfterInsertProcessing) {
isAfterInsertProcessing = true;
execute(new AccountAfterInsertTriggerHandler(), tp, TriggerParameters.TriggerEvent.afterInsert);
isAfterInsertProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.afterInsert);
}

/**

Called by the trigger framework to carry out the actions after the records are updated. If there is an
existing call running on the same context, the rentrant call will utilize the handler that was created
in the original call.
TriggerParameters Contains the trigger parameters which includes the record(s) that got updated.
*/
public virtual override void afterUpdate(TriggerParameters tp) {
if(!isAfterUpdateProcessing) {
isAfterUpdateProcessing = true;
execute(new AccountAfterUpdateTriggerHandler(), tp, TriggerParameters.TriggerEvent.afterUpdate);
isAfterUpdateProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.afterUpdate);
}

       public virtual override void afterDelete(TriggerParameters tp) {
if(!isAfterDeleteProcessing) {
isAfterDeleteProcessing = true;
execute(new AccountAfterDeleteTriggerHandler(), tp, TriggerParameters.TriggerEvent.afterDelete);
isAfterDeleteProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.afterDelete);
}


     public virtual override void afterUnDelete(TriggerParameters tp) {
if(!isAfterUnDeleteProcessing) {
isAfterUnDeleteProcessing = true;
execute(new AccountAfterUnDeleteTriggerHandler(), tp, TriggerParameters.TriggerEvent.afterUndelete);
isAfterUnDeleteProcessing = false;
}
else execute(null, tp, TriggerParameters.TriggerEvent.afterUndelete);
}
}


TriggerDispatcherBase.cls :
==============================
/**
 This class implements the ITriggerDispatcher and acts as an adapter to avoid implementing all the
 ITriggerDispatcher methods.
 @return Feedback__c The newly created feedback object.
*/
public virtual class TriggerDispatcherBase implements ITriggerDispatcher {

private static ITriggerHandler beforeInserthandler;
private static ITriggerHandler beforeUpdatehandler;
private static ITriggerHandler beforeDeleteHandler;
private static ITriggerHandler afterInserthandler;
private static ITriggerHandler afterUpdatehandler;
private static ITriggerHandler afterDeleteHandler;
private static ITriggerHandler afterUndeleteHandler;


    /**
This method is called prior to execution of a before trigger event. If you want
to load any lookup data or cache the data, then this is the place that you need
to put your code.
*/
    public virtual void bulkBefore() {}

    /**
This method is called prior to execution of an after trigger event.
*/
    public virtual void bulkAfter() {}

    /**
This method is called for records to be inserted during a BEFORE trigger.
*/
    public virtual void beforeInsert(TriggerParameters tp) {}

    /**
This method is called for records to be updated during a BEFORE trigger.
*/
    public virtual void beforeUpdate(TriggerParameters tp) {}

    /**
This method is called for records to be deleted during a BEFORE trigger.
*/
    public virtual void beforeDelete(TriggerParameters tp) {}

    /**
This method is called for records inserted during an AFTER trigger. Always put field validation
in the 'After' methods in case another trigger has modified any values. The record is 'read only'
at this point.
*/
    public virtual void afterInsert(TriggerParameters tp) {}

    /**
This method is called iteratively for each record updated during an AFTER trigger.
*/
    public virtual void afterUpdate(TriggerParameters tp) {}

    /**
This method is called iteratively for each record deleted during an AFTER trigger.
*/
    public virtual void afterDelete(TriggerParameters tp) {}
   
    /**
This method is called prior to execution of a AFTER UNDELETE trigger.
*/
    public virtual void afterUnDelete(TriggerParameters tp) {}
   
    public virtual void andFinally() {}
   
    /**
Called by the event handlers. If this is the first call in the context, then this method will create a new
instance of the appropriate handler and execute the mainEntry method. If there is an existing call running
on the same context, then this method will use the existing handler instance created by the original call
and execute the inProgressEntry method.
ITriggerHandler The trigger handler instance. The dispatcher need to pass an instance of the trigger handler, such
  as AccountAfterInsertTriggerHandler if this is the first call in a given context. If it is retry,
  then the dispatcher will need to pass null.
TriggerParameters The trigger parameters passed by the framework.
TriggerParameters.TriggerEvent The trigger event.
*/
    protected void execute(ITriggerHandler handlerInstance, TriggerParameters tp, TriggerParameters.TriggerEvent tEvent) {
    if(handlerInstance != null) {
    if(tEvent == TriggerParameters.TriggerEvent.beforeInsert)
    beforeInsertHandler = handlerInstance;
    if(tEvent == TriggerParameters.TriggerEvent.beforeUpdate)
    beforeUpdateHandler = handlerInstance;
    if(tEvent == TriggerParameters.TriggerEvent.beforeDelete)
    beforeDeleteHandler = handlerInstance;
    if(tEvent == TriggerParameters.TriggerEvent.afterInsert)
    afterInsertHandler = handlerInstance;
    if(tEvent == TriggerParameters.TriggerEvent.afterUpdate)
    afterUpdateHandler = handlerInstance;
    if(tEvent == TriggerParameters.TriggerEvent.afterDelete)
    afterDeleteHandler = handlerInstance;
    if(tEvent == TriggerParameters.TriggerEvent.afterUnDelete)
    afterUndeleteHandler = handlerInstance;
    handlerInstance.mainEntry(tp);
    handlerInstance.updateObjects();
    }
    else {
    if(tEvent == TriggerParameters.TriggerEvent.beforeInsert)
    beforeInsertHandler.inProgressEntry(tp);
    if(tEvent == TriggerParameters.TriggerEvent.beforeUpdate)
    beforeUpdateHandler.inProgressEntry(tp);
    if(tEvent == TriggerParameters.TriggerEvent.beforeDelete)
    beforeDeleteHandler.inProgressEntry(tp);
    if(tEvent == TriggerParameters.TriggerEvent.afterInsert)
    afterInsertHandler.inProgressEntry(tp);
    if(tEvent == TriggerParameters.TriggerEvent.afterUpdate)
    afterUpdateHandler.inProgressEntry(tp);
    if(tEvent == TriggerParameters.TriggerEvent.afterDelete)
    afterDeleteHandler.inProgressEntry(tp);
    if(tEvent == TriggerParameters.TriggerEvent.afterUnDelete)
    afterUndeleteHandler.inProgressEntry(tp);
    }
    }
}

ITriggerDispatcher.cls :
==========================

/**
 Defines the interface for the trigger dispatching architecture.
*/
public interface ITriggerDispatcher {
/**
Called by the trigger framework to carry out the actions before the bulk operations.
*/
void bulkBefore();

/**
Called by the trigger framework to carry out the actions after the bulk operations.
*/
void bulkAfter();

/**
Called by the trigger framework to carry out the actions after completing the bulk operations.
*/
void andFinally();

/**
Called by the trigger framework to carry out the actions before the records are inserted.
TriggerParameters Contains the trigger parameters which includes the records that is getting inserted.
*/
void beforeInsert(TriggerParameters tp);

/**
Called by the trigger framework to carry out the actions before the records are updated.
TriggerParameters Contains the trigger parameters which includes the records that is getting updated.
*/
void beforeUpdate(TriggerParameters tp);

/**
Called by the trigger framework to carry out the actions before the records are deleted.
TriggerParameters Contains the trigger parameters which includes the records that is getting deleted.
*/
void beforeDelete(TriggerParameters tp);

/**
Called by the trigger framework to carry out the actions after the records are inserted.
TriggerParameters Contains the trigger parameters which includes the records that got inserted.
*/
void afterInsert(TriggerParameters tp);

/**
Called by the trigger framework to carry out the actions before the records are updated.
TriggerParameters Contains the trigger parameters which includes the records that got updated.
*/
void afterUpdate(TriggerParameters tp);

/**
Called by the trigger framework to carry out the actions after the records got deleted.
TriggerParameters Contains the trigger parameters which includes the records that got deleted.
*/
void afterDelete(TriggerParameters tp);

/**
Called by the trigger framework to carry out the actions after the records are undeleted.
TriggerParameters Contains the trigger parameters which includes the records that got undeleted.
*/
void afterUnDelete(TriggerParameters tp);
}


ITriggerHandler.cls :
========================

/**
 Defines the interface for trigger handlers. Logic for the first time events are placed under the mainEntry
 method and the logic for the subsequent events raised on the same transaction (reentrant) are placed under
 the inProgressEntry method.
*/
public interface ITriggerHandler {
/**
Called for the first time in the execution context.
TriggerParameters The trigger parameters such as the list of records before and after the update.
*/
void mainEntry(TriggerParameters tp);

/**
Called for the subsequent times in the same execution context.
TriggerParameters The trigger parameters such as the list of records before and after the update.
*/
void inProgressEntry(TriggerParameters tp);

/**
Updates the objects, if any.
*/
void updateObjects();
}

TriggerHandlerBase.cls :
============================
/**
  This class implements the ITriggerHandler to provide abstract/virtual methods for the interface methods
  and so that the trigger handlers need to implement only the method that they have to. The only exception
  is the mainEntry, which is mandatory for the trigger handlers to implement.
*/
public abstract class TriggerHandlerBase implements ITriggerHandler {
protected Map<Id, SObject> sObjectsToUpdate = new Map<Id, SObject>();

/**
Called for the first time in the execution context. The trigger handlers need to implement this method.
TriggerParameters The trigger parameters such as the list of records before and after the update.
*/
public abstract void mainEntry(TriggerParameters tp);

/**
Called for the subsequent times in the same execution context. The trigger handlers can chose to ignore if they don't need the reentrant feature.
TriggerParameters The trigger parameters such as the list of records before and after the update.
*/
public virtual void inProgressEntry(TriggerParameters tp) {

}

/**
Updates the objects, if any.
*/
public virtual void updateObjects() {
if(sObjectsToUpdate.size() > 0)
update sObjectsToUpdate.values();
}
}


UtilityClass.cls :
=======================
/**
 This class has helper methods.
*/

public with sharing class UtilityClass {

/**
Gets the type name of the SObject.
@param SObject The SObject for which the name to be obtained.
@return String - The type name.
*/
public static String getSObjectTypeName(SObject so) {
return so.getSObjectType().getDescribe().getName();
}
}

AccountHelper.cls :
=====================

public with sharing class AccountHelper {

   public static map<Id,SObject> updateWebsite(list<Account> listNewAccounts){
map<Id,SObject> resultMap = new map<Id,SObject>();
for(Account acct : listNewAccounts) {
Account newAccount = new Account();
newAccount.Id = acct.Id;
newAccount.Website = 'www.salesforce.com';
resultMap.put(newAccount.Id, newAccount);
}
return resultMap;
}

}


TriggerParameters.cls :
=======================
/**
 This class holds the trigger parameters.
*/
public class TriggerParameters {

/**
A enum that represents the trigger event.
*/
public Enum TriggerEvent { beforeInsert, beforeUpdate, beforeDelete, afterInsert, afterUpdate, afterDelete, afterUndelete }
public TriggerEvent tEvent;

public List<SObject> oldList { get; private set; }
public List<SObject> newList { get; private set; }
public Map<Id, SObject> oldMap { get; private set; }
public Map<Id, SObject> newMap { get; private set; }
public String triggerObject { get; private set; }
public Boolean isExecuting { get; private set; }

/**
Constructs the TriggerParameter object.
@param List<SObject> A list of records with the state of 'before' event.
@param List<SObject> A list of records with the state of 'after' event.
@param Map<Id, SObject> A map of records with the state of 'before' event.
@param Map<Id, SObject> A map of records with the state of 'after' event.
@param Boolean A flag to indicate 'isBefore' event.
@param Boolean A flag to indicate 'isAfter' event.
@param Boolean A flag to indicate 'isDelete' event.
@param Boolean A flag to indicate 'isInsert' event.
@param Boolean A flag to indicate 'isUpdate' event.
@param Boolean A flag to indicate 'isUnDelete' event.
@param Boolean A flag to indicate 'isExecuting'.
*/
public TriggerParameters(List<SObject> olist, List<SObject> nlist, Map<Id, SObject> omap, Map<Id, SObject> nmap,
Boolean ib, Boolean ia, Boolean id, Boolean ii, Boolean iu, Boolean iud, Boolean ie) {
this.oldList = olist;
this.newList = nlist;
this.oldMap = omap;
this.newMap = nmap;
this.triggerObject = UtilityClass.getSObjectTypeName((this.oldList != null && this.oldList.size() > 0) ? this.oldList[0] : this.newList[0]);
if (ib & ii) tEvent = TriggerEvent.beforeInsert;
else if (ib && iu) tEvent = TriggerEvent.beforeUpdate;
else if (ib && id) tEvent = TriggerEvent.beforeDelete;
else if (ia && ii) tEvent = TriggerEvent.afterInsert;
else if (ia && iu) tEvent = TriggerEvent.afterUpdate;
else if (ia && id) tEvent = TriggerEvent.afterDelete;
else if (ia && iud) tEvent = TriggerEvent.afterUndelete;
isExecuting = ie;
}
}

AccountBeforeInsertTriggerHandler.cls :
========================================

public class AccountBeforeInsertTriggerHandler extends TriggerHandlerBase
 {
public override void mainEntry(TriggerParameters tp)
        {

}
}

AccountBeforeUpdateTriggerHandler.cls :
=======================================

public class AccountBeforeUpdateTriggerHandler extends TriggerHandlerBase
{
public override void mainEntry(TriggerParameters tp)
        {

}
}
AccountAfterInsertTriggerHandler.cls :
========================================
public class AccountAfterInsertTriggerHandler extends TriggerHandlerBase
{
public override void mainEntry(TriggerParameters tp)
         {

}
}

AccountAfterUpdateTriggerHandler.cls :
=======================================

public class AccountAfterUpdateTriggerHandler extends TriggerHandlerBase
{

public override void mainEntry(TriggerParameters tp) {
process((List<Account>)tp.newList);
}

private void process(List<Account> listNewAccounts) {
sObjectsToUpdate.putAll(AccountHelper.updateWebsite(listNewAccounts));
}

public override void inProgressEntry(TriggerParameters tp) {
System.debug('This is an example for reentrant code...');
}

public override void updateObjects() {
// for demonstration purposes, don't do anything here...
}
}

TriggerException.cls :
======================

public class TriggerException extends Exception {

}

Wrapper class in Apex


A Wrapper is simply an instance of a class that exposes some or all of the properties of a contained object,an object-oriented nomenclature.

ex :

public class MyWrapper
{

public Contact con {get;private set;}
public Opportunity opp {get;private set;}
public MyWrapper(Contact c,Opportunity o)
  {
     this.con=c;
     this.opp=o;
  }
}

ex 2 :


public class SObjectWrapper
{
  public SObject sObj {get;private set;}
  public Object name {get;private set;}
  Public Boolean deleteThis {get;private set;}
 //constructor
  Public SObjectWrapper(SObject so)
   {
     this.sObj=so;
     try{
          this.name=this.sObj.get('Name');
        }
        catch(Exception e){}
  }
}

public string selectString {get;set;}
public List<SObjectWrapper> sObjectWrpper {get;private set;}

public PageReference executeSelect()
{
   try{
      loadData(Database.query(this.selectString));
    }
   catch(Exception e)
   {
     logError(e.getMessage());
   }

}

private void loadData(List<SObject> sObjs)
{
  this.sObjectWrapperList = new List<SObjectWrapper>();
   for(SObject s : sObjs)
    {
    this.sObjectWrapperList.add(new SObjectWrapper(s));
   
   }
}

public PageReference deleteSelected()
{
  try
  {
    List<SObject> sObjs=new List<SObject>();
     for (SObjectWrapper so : this.sObjectWrapperList)
       {
         if(so.deleteThis==true)
            sObjs.add(so.sObj);
       }
     delete sObjs;
     executeSelect();
  }
  catch(Exception e)
   {
   logError(e.getMessage());
   }
  return null;
}

private void logError(String errorString)
{

 ApexPages.addMessage(new ApexPages.Message(ApexPages.severity.ERROR,errorString);
}

bulk processing patterns in Apex


bulk processing patterns in Apex :
=================================
All of your Apex code should be designed to handle bulk operations.All of it-no exceptions.

1.Apex triggers are always bulk.
2.Avoid governor limits.

common trigger design :
=======================
the order of trigger execution is guaranteed to be unpredictable.

managing recursion :
=====================

public with sharing class IsProcessing
{
  public static Boolean isProcessing=false;
}

public with sharing class TestIsProcessing
{
  public void processLogic()
  {
    public void processLogic()
     {
       if(!IsProcessing.isProcessing)
        {
          IsProcessing.isProcessing=true;
           //--- do processing
        }
     }

  static testMethod void testProcessing()
  {
   system.assert(!IsProcessing.isprocessing);
   TestIsProcessing tip =new TestIsProcessing();
    tip.processLogic();
   system.assert(IsProcessing.isProcessing);
  }
  }

}

The most common Apex pattern for dealing with recursion is by leveraging a public
static Boolean somewhere in a class.Then,either in trigger code or in a class method called by the trigger,the static Boolean is evaluated immediately when the trigger code first executes.If the value
is false,then it is immediately set to true when processing continues.

Note : static variables in apex are only global to the specific execution context that reference them.

Interface based centralized trigger dispatching architecture :
==========================================

Apex supports Interfaces,abstract classes and virtual classes.

ex : MainContactEntry.trigger

trigger MainContactEntry on Contact (before insert,before update,before delete,
after insert,after update,after delete)
{
  TriggerDispatcher.entry(new TriggerDispatcher.TriggerParameters(Trigger.isBefore,
     Trigger.isAfter,Trigger.isDelete,trigger.isInsert,Trigger.isUpdate,
     Trigger.isExecuting,Trigger.old,Trigger.new,Trigger.oldMap,Trigger.newMap));

}

TriggerDispatchr.cls

public with sharing class TriggerDispatcher
{

// contacts
  private static final String SACCOUNT='Account';
  private static final String SCONTACT='Contact';
  private static final String SLEAD = 'Lead';
  private static final String SOPPORTUNITY ='Opportunity';
  private static final String SUSER ='User';

  public static ITriggerEntry activeInstance = null;
  public static Map<Id,Sobject> sObjectsToUpdate =new Map<Id,Sobject>();

  public interface ITriggerEntry
   {
     void mainEntry(TriggerParameters tp);
     void inProgressEntry(TriggerParameters tp);
   }

  public class TriggerParameters
  {
    public String triggerObject {get;private set;}
    public Boolean isBefore {get;private set;}
    Public Boolean isAfter  {get;private set;}
    Public Boolean isDelete {get;private set;}
    public Boolean isInsert {get;private set;}
    public Boolean isUpdate {get;private set;}
    public Boolean isExecuting {get;private set;}
    public List<Sobject> oldList {get;private set;}
    public List<Sobject> newList {get;set;}
    public Map<Id,Sobject> oldMap {get;private set;}
    public Map<Id,Sobject> newMap {get;set;}
 
  public TriggerParameters(Boolean ib,Boolean ia,Boolean id,Boolean ii,Boolean iu,Boolean ie,
                   List<Sobject> ol,List<Sobject> nl,Map<Id,Sobject> om,map<Id,Sobject> nm)
    {
      this.isBefore = ib;
      this.isAfter  = ia;
      this.isDelete = id;
      this.isInsert = ii;
      this.isUpdate = iu;
      this.isExecuting = ie;
      this.oldList = ol;
      this.newList = nl;
      this.oldMap = om;
      this.newMap = nm;
      this.triggerObject = getSobjType( ( this.oldList !=null && this.oldList.size() >0 )?
           this.oldList[0] : this.newList[0] );
    }
 

 private String getSobjectType (Sobject so)
  {
    String retVal;
    if(so instanceof Account)  retVal = SACCOUNT;
    else if (so instanceof Contact) retVal = SCONTACT;
    else if (so instanceof Lead) retVal = SLEAD;
    else if (so instanceof Opportunity) retVal= SOPPORTUNITY;
    else if (so instanceof User) retVal = SUSER;
    return retVal;
  }
}

// Central dispatch entry

 public static void entry(TriggerParameters tp)
  {
     if(activeInstance == null)
          processWork(tp);
     else
       activeInstance.inProgressEntry(tp);
  }

 // Order of execution is controlled
  private static void processWork(TriggerParameters tp)
   {
      if(tp.triggerObject == SContact && tp.isAfter && tp.isUpdate)
       {
         execute(new SyncContactToUser(), tp);
       }
       else if(tp.triggerObject == SUSER && tp.isAfter && tp.isUpdate)
         execute(new SyncUserToContact(), tp);
     
       //update records
       if(sObjectsToUpdate.size() >0)
         update sObjectsToUpdate.values(); 
   }

  private static void execute (ITriggerEntry ite, TriggerParameters tp)
   {
     activeInstace = ite;
     activeInstance.mainEntry(tp);
   }

}


Note :
The instanceof keyword allows you to check the if an object is an instance of a class.

SyncContactToUser.cls :
========================

public with sharing class SyncContactToUser implements TriggerDispatcher.ITriggerEntry
{
  public void inProgressEntry(TriggerDispatcher.TriggerParameters tp)
  {
    // confirm caller origin
    system.assert(tp.triggerObject=='User');
  }
  public void mainEntry(TriggerDispatcher.TriggerParameters tp)
   {

   Map<Id,Contact> newMap =(Map<Id,Contact>) tp.newMap;
   List<Id> userIds = new List<Id>();
   for(Contact c : newMap.values())
      {
        userIds.add(c.User__c);
      }
    Map<Id,User> userMap = new Map<Id,User>
   ([SELECT Id,Phone,MobilePhone,Fax FROM User WHERE Id IN : userIds]);
   
    for(Contact c : newMap.values())
    {
      User u=userMap.get(c.User__c);
      if(u.Phone !=c.Phone || u.MobilePhone !=c.MobilePhone || u.Fax !=c.Fax)
        {
           u.Email=c.Email;
           u.Phone=c.Phone;
           u.MobilePhone =c.MobilePhone;
           u.Fax = c.Fax;
           TriggerDispatcher.sObjectsToUpdate.put(u.Id,u);
         }
    }
  }
}

SyncUserToContact.cls :
=========================

public with sharing class SyncUserToContact implements TriggerDispatcher.ITriggerEntry
{

 public void inProgressEntry(TriggerDispatcher.TriggerParameters tp)
  {
    system.assert(tp.triggerObject == 'Contact');
  }

 public void mainEntry(TriggerDispatcher.TriggerParameters tp)
  {
    Map<Id,User> newMap=(Map<Id,User>) tp.newMap;
    List<Contact> contactList = [SELECT User__c,Phone,MobilePhone,Fax FROM Contact WHERE User__c IN : newMap.keyset()];
     
      for(Contact c : contactList)
        {
           User u = newmap.get(c.User__c);
            if(c.Phone!=u.Phone || c.MobilePhone !=u.MobilePhone || c.Fax!=u.Fax)
              {
                c.Phone=u.Phone;
                c.MobilePhone=u.MobilePhone;
                c.Fax=u.Fax;
                TriggerDispatcher.sObjectsToUpdate.put(c.Id,c);
              }
        }
  }

}

Asynchronous Execution :
==========================
The three available tools that you can leverage for Asychronous Execution on force.com.

1.Scheduled Apex
2.Batch Apex
3.@Future calls

Through the standard interface(declarative tool) ,  you can activate schedulable Apex classes
and configure the execution recurrence.

The available recurrence options are very flexible,permitting schedules such as execute every Monday,Wednesday and Friday at 5 p.m. or execute  at 11 a.m on third Thrusday of every month.

Limitation to use the standard interface to schedule Apex classes.You're limited to one execution per day per configured schedule.

Note : 25 is the maximum number of  Apex classes that can be schedule at any one time,this poses a real problem.

if you schedule your Apex classes to run from within Apex that can execute on the hour if needed.

Scheduled Apex is only targeted to execute on a set schedule,but depending on available resources,they execute after the scheduled time.And only 25 concurrent Scheduled Apex processes
maybe active at any one time regardless of their recurrence frequency.

Future calls :
==============
Within a single execution context, we can only call 10 future methods and within each future method we're limited to 10 callouts.

1.Runs in its own execution context
2.Certain limits are increased
3.Limited to 10 calls per context
4.Limited to 200 calls per salesforce
user license,per 24 hours
5.Can't be called from Batch Apex or
future method.

Batch Apex :
==============

1.Runs in its own execution context.
2.Certain limits are increased.
3.can process up to 50 million records.
4.Batch sizes of 1-2000 records
5.Up to 250,000 daily Batch Apex Executions permitted.
6.Maximum 5 concurrent batch Apex processes.
7.Transactions cannot span batch processes in Apex.

In other words, after each batch of 1 to 2,000 objects has been committed, this cannot be undone in a subsequent batch.

8.Can't be called from Batch Apex or future methods.

There are a few ways to work around this using other techniques such as email services and chaining by way of using Scheduled Apex to bridge subsequent Batch Apex calls.