Settings Reference


Best Practices and Recommendations

Best Practices and Recommendations

A big organization with many business units asking and submitting requests and enhancements may push developers wanting to do a good job in the back end to put a lot of spaghetti code ( I don't want to point out fingers when i see a bad implementation because i understand business is fast pace moving and wants results in lighting frame time hence reducing assessment and even development time and a good solution takes some time to develop ).

By having a trigger framework flexible enough where the business logic is 100% isolated from the trigger implementation (featuring ATARC) along with mindset and a set of rules to follow to assure an architecture that when developing a solution within salesforce which requires trigger related code developers will stick to best practices.

Use A trigger Framework

Using a trigger frameworks is a good starting point so that developers focus more on the business logic itself as the framework is already built. A good trigger framework will most likely reduce functionalities development time as well as the implementation should be smoother, and this is a major point.

Recommended General Trigger Execution Rules

The following are special recommendations specifically designed for a better performance and overall trigger execution fine tuning. What you should and should not do for before and after events during a trigger execution.


For BEFORE Events

DON'T
  • DML Statements (insert, update, delete, etc...)
  • Submit approval process
  • Http Callouts
  • Chatter Posts
  • Email Notifications
  • Any type of notifications or messaging
  • Any type of actions that could trigger anything in an external system (integrations, consuming external apis)
DO
  • Record validations (addError calls) or throwing any exception
  • Assign value to a record's field or field updates
For AFTER Events

DON'T
  • Record validations when using "async" feature, the ideal is that processes attached to "before" events handle the validations (add error calls wont work if "async" feature is set using atarc framework ).
  • Assign value to a record's field or field updates (this actually throws an error)
DO
  • Record validation only when not using "async" feature if it is extremely necessary.
  • Submit approval process only when not using "async" feature if you want the users to see any approval process error. If you don't care if when submitting a record for approval you get validation errors then you can make the process async to submit the approval process.
  • DML Statements (insert, update, delete, etc..) always ideally with "async" feature.
  • Http callouts ideally with "async" feature.
  • Chatter post ideally with "async" feature.
  • Email notifications ideally with "async" feature.
  • Any type of notifications or messaging ideally with "async" feature
  • Any type of actions that could trigger anything in an external system ideally with "async" feature.

ATARC Best Practices


About Apex Triggers

One trigger per object is enough. The trigger should be attached to all the events, look at the following example:


trigger ATARCOpportunityTrigger on Opportunity (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
    
            new AsyncTriggerArc().start();

}
        

If more than one trigger, do not attach two triggers to the same events:


trigger ATARCOpportunityBeforeTrigger on Opportunity (before insert, before update, before delete) {
    
            new AsyncTriggerArc().start();

}
        

trigger ATARCOpportunityAfterTrigger on Opportunity (after insert, after update, after delete, after undelete) {
    
            new AsyncTriggerArc().start();

}
        

Don't ever have more than one apex trigger attach to the same events.

About Atarc Process
  • Atarc Process (apex class extending the AsyncTriggerArc.AsyncTriggerArcFEAProcessBase or AsyncTriggerArc.AsyncTriggerArcProcessBase abstract classes) should be focus on a single responsibility. This way you will be able to have full control over Atarc Processes features.
  • Use ATARC's triggerContext variable always in order to access the records being processed and more trigger variables. Avoid using "trigger" standard class. See the Api Reference to know more about the list of members inside the Atarc Process's triggerContext parameter.

public class OpptyScoreNotificationProcess extends AsyncTriggerArc.AsyncTriggerArcProcessBase {

    protected override object execute(AsyncTriggerArc.AsyncTriggerArcContext triggerContext)
    {
      List<Opportunity> oppties = (List<Opportunity>)triggerContext.newList;
      if(triggerContext.IsBefore) triggerContext.debug('Running from before ');
      if(triggerContext.IsInsert) triggerContext.debug('This is an insert..'+oppties.size());
      if(triggerContext.IsUpdate) triggerContext.debug('This is an update..'+oppties.size());
      
       //.. and so on..
       return null;
        
    }

}
            
  • Use ATARC's trigger context debug method always to print to the debug log.
  • Use AsyncTriggerArc.debug from helpers classes whenever is needed to try to write to the debug log from outside. this will behave as triggerContext.debug. This means that if this helper is called from an Atarc Process, the debug flag from the process can control the output to the debug log.

public class DummyHelper {
    public static void Calculate(){
        integer result=1+2;
        AsyncTriggerArc.debug('this is the result = '+result);
    }

}
            
  • If you want to make sure an Atarc Process is marked as "Failed" if the result of the operation is not as expected, call setFailed method.

//FEA pattern base class, this can be done using the regular base class as well
public class OpptyScoreCalculationProcess extends AsyncTriggerArc.AsyncTriggerArcFEAProcessBase {
    
    Set oppties = new Set();

    protected override void filter(sObject oldRecord, sObject newRecord, AsyncTriggerArc.AsyncTriggerArcContext triggerContext){
      Opportunity oldR = (Opportunity)oldRecord;
      Opportunity newR = (Opportunity)newRecord;

      //called one time per each record
      if(oldR.stageName != newR.StageName)
          oppties.add(newR.Id);
    }
    
    protected override object execute(AsyncTriggerArc.AsyncTriggerArcContext triggerContext)
    {  
       if(oppties.size() <= 0){
          //allways do this if the result is not the expected
          //and you want ATARC to know this is a fail
          triggerContext.setFailed();
        }  
        
       return null;
        
    }
    
    protected override void action(sObject oldRecord, sObject newRecord, AsyncTriggerArc.AsyncTriggerArcContext triggerContext){
      //called one time per each record
    }

}
            
  • When using async processes avoid going beyond 4 level of async (queueable) chaining since salesforce has a hard limit of 5 async depth, the sixth attempt of depth will throw an error.
  • Try to put a sufix to your Atarc Process apex classes, "_AP" or similar (i.e HashGeneartor_AP), that way you can easily identify them just by convention.