Trigger design is one of the most important things to get right when writing Apex code. Unfortunately, many developers don't know they're not getting it right until suddenly, things goes very wrong... usually in production! Join us as we explore the 'what and why' of trigger best practices, and introduce a secret weapon for the how: The Simple Trigger Pattern. We will demonstrate how this collection of free, reusable, open-source Apex classes gives you an easy-to-understand framework that encapsulates core best practices. You'll walk away understanding why these best practices are so important, and how to find, download, and implement the Simple Trigger Pattern in your own orgs.
5. ââŻTrigger best practices are important because trigger logic is usuallyâŚ
â˘âŻ Complex
â˘âŻ Often mission critical!
â˘âŻ Entwined with other logic
â˘âŻ You donât always control the other logic!
â˘âŻ High Volume
â˘âŻ Build for scale nowâŚDonât play catch-up later!
Why is This Important?
6. trigger AccountValidation on Account
(before insert, before update) {
// Do validation stuff...
}
trigger AccountRollup on Account
(after insert, after update) {
// Do rollup stuff...
}
trigger ShadowAccountBuilder on Account
(after insert, after update) {
// Do contact building stuff...
}
ââŻCore Problems
â˘âŻ Unknown order of execution
â˘âŻ Duplicated business logic
â˘âŻ Code is harder to maintain
Best Practice #1
ââŻâOne Trigger Per Objectâ
7. trigger AccountTrigger on Account
(before insert, before update,
after insert, after update) {
if (Trigger.isBefore && Trigger.isInsert) {
// Do validation stuff...
}
else if (Trigger.isBefore && Trigger.isUpdate) {
// Do validation stuff...
}
else if (Trigger.isAfter && Trigger.isInsert) {
// Do rollup stuff...
// Do shadow account stuff...
}
else if (Trigger.isAfter && Trigger.isUpdate) {
// Do rollup stuff...
// Do shadow account stuff...
}
}
ââŻRecommendations
â˘âŻ DeďŹne a single âmasterâ trigger
â˘âŻ Capture all âtrigger actionsâ in the master trigger
â˘âŻ Use trigger context variables to determine what
trigger action is being executed
Best Practice #1
ââŻâOne Trigger Per Objectâ
8. trigger AccountTrigger on Account
(before insert, before update,
after insert, after update) {
if (Trigger.isBefore && Trigger.isInsert) {
// Do validation stuff...
}
else if (Trigger.isBefore && Trigger.isUpdate) {
// Do validation stuff...
}
else if (Trigger.isAfter && Trigger.isInsert) {
// Do rollup stuff...
// Do shadow account stuff...
}
else if (Trigger.isAfter && Trigger.isUpdate) {
// Do rollup stuff...
// Do shadow account stuff...
}
}
ââŻCore Problems
â˘âŻ Code is harder to maintain
â˘âŻ Static variables arenât available in trigger ďŹles
Best Practice #2
ââŻâNo Business Logic in Triggersâ
9. trigger AccountTrigger on Account
(before insert, before update,
after insert, after update) {
AccountTriggerHandler
handler = new AccountTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.beforeInsert();
}
else if (Trigger.isBefore && Trigger.isUpdate) {
handler.beforeUpdate();
}
else if (Trigger.isAfter && Trigger.isInsert) {
handler.afterInsert();
}
else if (Trigger.isAfter && Trigger.isUpdate) {
handler.afterUpdate();
}
}
ââŻRecommendations
â˘âŻ Implement a Trigger Handler Class
â˘âŻ Dispatch to speciďŹc âAction Handlerâ methods
â˘âŻ Trigger Handler should leverage Service Classes
for greater reusability
Best Practice #2
ââŻâNo Business Logic in Triggersâ
10. public class AccountTriggerHandler {
public void
afterInsert(list<Account> newAccounts,
map<Id, Account> newAccountsMap) {
// Create shadow accounts
list<Account> shadowAccs = new list<Account>();
for (Account newAccount : newAccounts) {
Account shadowAcc = new Account();
shadowAcc.Name = newAccount.Name
+ â (Shadow)â;
shadowAccs.add(shadowAccount);
}
insert shadowAccs;
}
}
ââŻCore Problem
â˘âŻ Something causes your trigger logic to execute
more than once
â˘âŻ Trigger performs DML on a same-type object
â˘âŻ WorkďŹow ďŹres that causes a ďŹeld update
Best Practice #3
ââŻâPrevent Recursion Using Static Variablesâ
11. public class AccountTriggerHandler {
// Define a recursion check variable.
private static boolean aiFirstRun = true;
public void
afterInsert(list<Account> newAccounts,
map<Id, Account> newAccountsMap) {
// Check the âfirst runâ flag.
if (aiFirstRun == false) {
return;
}
// Flip the âfirst runâ flag.
aiFirstRun = false;
// Create shadow accounts...
createShadowAccounts(newAccounts);
}
}
ââŻRecommendations
â˘âŻ Use static variables to create a âgatekeeperâ for
your trigger logic
â˘âŻ Solves recursion issues in large majority of use
cases
â˘âŻ Bonus Tip: Use private helper methods to keep
handler methods semantically clean and direct
Best Practice #3
ââŻâPrevent Recursion Using Static Variablesâ
12. ââŻBulkiďŹed Code is not a trigger best practice...
ââŻBulkiďŹed Code is an Apex best practice!
â˘âŻ BulkiďŹcation means your code can handle batches
of 200 records
â˘âŻ An Apex developer should always think âbulk
ďŹrstâ
â˘âŻ Service and Utility methods should be âbulkiďŹed
from birthâ
Wait a MinuteâŚWhat About BulkiďŹcation?
14. ââŻMany Excellent Alternatives
â˘âŻ Tidy Pattern
â˘âŻ Simple Trigger Template
â˘âŻ TriggerX
â˘âŻ Hari Krishnanâs Framework
â˘âŻ Andrew Fawcettâs SOC
â˘âŻ Just to name a few...
WaitâŚDo we Really Need Another Trigger Pattern?
Simple
Trigger
Pattern
15. â˘âŻ One parent class
â˘âŻ One test class
â˘âŻ One Apex trigger template
â˘âŻ One Apex class template
â˘âŻ Encapsulates multiple best
practices
â˘âŻ Enables rapid development
â˘âŻ Provides a straightforward
standard thatâs easy to
implement
ââŻGitHub Repository
â˘âŻ bit.ly/SimpleTriggerPattern
ââŻGitHub Gist
â˘âŻ bit.ly/SimpleTriggerPatternGist
What Is It? What Does It OďŹer? Where Can I Get It?
Introducing the Simple Trigger Pattern (STP)
17. ââŻThree critical best practices for writing Apex triggers
â˘âŻ âOne trigger per objectâ
â˘âŻ âNo business logic in triggersâ
â˘âŻ âPrevent recursion using static variablesâ
ââŻIntroduced the Simple Trigger Pattern
ââŻLearned where to get and how to use the code
Review