Apex - Trigger Design Patterns

I modelli di progettazione vengono utilizzati per rendere il nostro codice più efficiente e per evitare di raggiungere i limiti del governor. Spesso gli sviluppatori possono scrivere codice inefficiente che può causare ripetute istanze di oggetti. Ciò può comportare un codice inefficiente, con prestazioni scarse e potenzialmente la violazione dei limiti del governatore. Ciò si verifica più comunemente nei trigger, poiché possono operare su un set di record.

In questo capitolo vedremo alcune importanti strategie di design pattern.

Modelli di progettazione di trigger in blocco

Nel caso aziendale reale, sarà possibile che tu debba elaborare migliaia di record in una volta sola. Se il trigger non è progettato per gestire tali situazioni, potrebbe non riuscire durante l'elaborazione dei record. Esistono alcune best practice che è necessario seguire durante l'implementazione dei trigger. Tutti i trigger sono trigger in blocco per impostazione predefinita e possono elaborare più record alla volta. Dovresti sempre pianificare l'elaborazione di più di un record alla volta.

Considera un caso aziendale, in cui è necessario elaborare un gran numero di record e aver scritto il trigger come indicato di seguito. Questo è lo stesso esempio che avevamo preso per inserire il record della fattura quando lo Stato del cliente cambia da Inattivo ad Attivo.

// Bad Trigger Example
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   
   for (APEX_Customer__c objCustomer: Trigger.new) {
      
      if (objCustomer.APEX_Customer_Status__c == 'Active' && 
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         
         // condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         insert objInvoice;   //DML to insert the Invoice List in SFDC
      }
   }
}

Ora puoi vedere che l'istruzione DML è stata scritta per il blocco loop che funzionerà quando si elaborano solo pochi record ma quando si elaborano alcune centinaia di record, raggiungerà il limite dell'istruzione DML per transazione che è il governor limit. Avremo uno sguardo dettagliato sui limiti del governatore in un capitolo successivo.

Per evitare ciò, dobbiamo rendere il trigger efficiente per l'elaborazione di più record alla volta.

Il seguente esempio ti aiuterà a capire lo stesso:

// Modified Trigger Code-Bulk Trigger
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   
   for (APEX_Customer__c objCustomer: Trigger.new) {
      
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         
         //condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);//Adding records to List
      }
   }
   
   insert InvoiceList;
   // DML to insert the Invoice List in SFDC, this list contains the all records 
   // which need to be modified and will fire only one DML
}

Questo trigger attiverà solo 1 istruzione DML poiché funzionerà su una lista e la lista ha tutti i record che devono essere modificati.

In questo modo, puoi evitare i limiti del governor dell'istruzione DML.

Classe assistente trigger

Anche scrivere l'intero codice in trigger non è una buona pratica. Quindi è necessario chiamare la classe Apex e delegare l'elaborazione da Trigger alla classe Apex come mostrato di seguito. La classe Trigger Helper è la classe che esegue tutte le elaborazioni per il trigger.

Consideriamo nuovamente il nostro esempio di creazione di record di fattura.

// Below is the Trigger without Helper class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   
   for (APEX_Customer__c objCustomer: Trigger.new) {
      
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         
         // condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);
      }
   }
   
   insert InvoiceList; // DML to insert the Invoice List in SFDC
}

// Below is the trigger with helper class
// Trigger with Helper Class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   CustomerTriggerHelper.createInvoiceRecords(Trigger.new, trigger.oldMap);
   // Trigger calls the helper class and does not have any code in Trigger
}

Classe di aiuto

public class CustomerTriggerHelper {
   public static void createInvoiceRecords (List<apex_customer__c>
   
   customerList, Map<id, apex_customer__c> oldMapCustomer) {
      List<apex_invoice__c> InvoiceList = new Listvapex_invoice__c>();
      
      for (APEX_Customer__c objCustomer: customerList) {
         
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            oldMapCustomer.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            
            // objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      
      insert InvoiceList;  // DML to insert the Invoice List in SFDC
   }
}

In questo, tutta l'elaborazione è stata delegata alla classe helper e quando abbiamo bisogno di una nuova funzionalità possiamo semplicemente aggiungere il codice alla classe helper senza modificare il trigger.

Trigger singolo su ogni oggetto

Crea sempre un singolo trigger su ogni oggetto. Più trigger sullo stesso oggetto possono causare il conflitto e gli errori se raggiunge i limiti del governor.

È possibile utilizzare la variabile di contesto per chiamare i diversi metodi dalla classe helper secondo il requisito. Considera il nostro esempio precedente. Supponiamo che il nostro metodo createInvoice debba essere chiamato solo quando il record viene aggiornato e su più eventi. Quindi possiamo controllare l'esecuzione come di seguito -

// Trigger with Context variable for controlling the calling flow
trigger Customer_After_Insert on APEX_Customer__c (after update, after insert) {
   
   if (trigger.isAfter && trigger.isUpdate) {
      // This condition will check for trigger events using isAfter and isUpdate
      // context variable
      CustomerTriggerHelper.createInvoiceRecords(Trigger.new);
      
      // Trigger calls the helper class and does not have any code in Trigger
      // and this will be called only when trigger ids after update
   }
}

// Helper Class
public class CustomerTriggerHelper {
   
   //Method To Create Invoice Records
   public static void createInvoiceRecords (List<apex_customer__c> customerList) {
      
      for (APEX_Customer__c objCustomer: customerList) {
         
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      
      insert InvoiceList; // DML to insert the Invoice List in SFDC
   }
}