What is Bulk State Transition?: This pattern is used to perform bulk actions efficiently based on change of state of one or more records.
Why Bulk State Transition?: We generally creates a utility to process bulk records here. Same utility can be used anywhere irrespective of triggers.
Problem: We will taking an example of order creation based on when opportunity is closed. Below code handles state transition (i.e. only if the opportunity is closed). Also, it’s bulkified – separate list to keep track of orders, and a single bulk insert. However, the order class is highly coupled and is very hard to reuse outside of the trigger context. You can pass in null into the second argument, but that relies on you knowing the inner workings of the class.
trigger OpptyTrigger on Opportunity (before insert, before update) {
if (trigger.isInsert) {
new OrderClass().CreateOrder(trigger.newMap, null);
else
new OrderClass().CreateOrder(trigger.newMap, trigger.oldMap);
}
}
public class OrderClass {
public void CreateOrder(Map<Opportunity> opptyMapNew
Map<Opportunity> opptyMapOld) {
List<Order__c> orderList = new List<Order__c>();
for (Opportunity oppty : opptyMapNew.values()) {
if (oppty.isClosed && (opptyMapOld == null ||
!opptyMapOld.get(oppty.id).isClosed)) {
Order__c order = new Order__c();
...
orderList.add(order);
}
}
insert orderList ;
}
}
Solution: We will use the bulk state transition pattern which does the following-
- The trigger checks for eligible records that have changed state
- Calls a utility class to perform the work, only passing in the eligible records
- The utility class has a method that does the work in bulk
trigger OpptyTrigger on Opportunity (after insert, after update) {
if (trigger.isAfter && (trigger.isInsert || trigger.isUpdate)) {
List<Opportunity> closedOpptyList = new List<Opportunity>();
//loop through opportunities and check if closed
for (Opportunity oppty : trigger.new) {
/* Note: for insert, check current state,
for update, check current state and prior state */
if (oppty.isClosed && (trigger.isInsert ||
(trigger.isUpdate &&
!trigger.oldMap.get(oppty.id).isClosed)))
closedOpptyList.add(oppty);
}
//send eligible records to OrderClass
if (!closedOpptyList.isEmpty())
new OrderClass().CreateOrderFromOpptys(closedOpptyList);
}
}
public class OrderClass {
public void CreateOrdersFromOpptys(List<Opportunity> opptyList) {
List<Order__c> orderList = new List<Order__c>();
for (Opportunity oppty : opptyList) {
Order__c order = new Order__c();
//more logic here
orderList.add(order);
}
insert orderList ;
}
}
For all Design Patterns, please refer this.