05 Mar 2022
In Salesforce Apex, you can loop over
Trigger.new as many times as you’d like.
Yes, you should only have one Apex Trigger per Salesforce object. Having redundant ones makes for an unpredictable runtime.
No, you don’t need to try to shove all business logic pertaining to a given Salesforce object into one single “trigger handler” Apex Class.
That’s a thing I actually used to do, when I first started writing Apex and was a new programmer.
In reality, it’s way more “modular,” and therefore simpler to maintain over the years, if you create a bazillion trigger handler classes per object, each of which perform their own
for loop over
It’s relatively computationally cheap to loop over
Trigger.new. Break up your trigger handlers into discrete clumps of business logic and don’t look back.
The amount of CPU time it takes to re-instantiate objects and variables is nothing compared to the amount of human time it takes to edit a messy codebase.
For starters, as a beginning developer in a small org, you can just invoke all classes for a given object back-to-back in your trigger. As your complexity starts to grow, refactor your handler classes to be compatible with a trigger framework and just call the framework from each trigger.
Or, if you’re an experienced programmer starting out in a greenfield org, just pick a trigger framework and write your trigger-handler Apex classes to conform to it from the get-go. You know what you’re doing, so you probably won’t find it too hard to structure your handler codebases so you can pick a different one and refactor later. Salesforce recommends Kevin O’Hara’s as a starting point, unless your org has NPSP or EDA installed, in which case you should piggyback onto the TDTM functionality written into those packages.
Separation of concerns
Also, if you follow Andy Fawcett’s principle of “separating concerns” and further splitting your trigger handler classes apart so that all of the interesting business logic is done in a standalone “application service layer” class that would be just as happy being invoked from a Lightning Web Component, a Scheduled Batch, etc. as it is being invoked from a trigger handler, then you’ll have an even easier time refactoring your trigger handler classes to conform to a different framework later, should you so choose.
Business logic should be encapsulated independent of how it’s invoked.
When you follow Andy’s concept, “what’s supposed to happen when the trigger runs” (application) logic goes in one Apex class, and “make the trigger run correctly” (trigger-handler) logic goes into another Apex class.
I found Andy’s Unit of Work pattern for DML to be far heaver weight than I needed, but the concept of breaking DML and the process of obtaining DML-ready SObjects away from any code covering “business logic” is something I found important in following Andy’s Separation of Concerns princple.
I ended up using Dan Appleman’s “Simple…Updater” pattern in his Pluralsight course with Don Robins, Play by Play: Adopting Trigger Design Patterns in Existing Salesforce Orgs (under “Trigger Patterns Without Frameworks” > “Combining DML Operations”).
Dan demonstrated w/ an Apex class called
SimpleLeadUpdater, which I then refactored into a suite of 6 classes. I found these 6 classes simpler to implement than Andy’s Unit of Work, and yet they still played well with Andy’s other big ideas:
Request: If you use these, please leave in the comments steering people toward Katie Kodes and toward Dan Appleman’s paid products.
Since you’ll surely be using classes like DML helpers throughout your codebase, be sure to either:
- Modularize your DML helpers into an actual Salesforce package (thanks Jesse Rymph), or
- Modularize your DML helpers into a reusable package-type structure like a CumulusCI project
Then include it as a dependency within other codebases, rather than literally copying & pasting your DML helper codebases into other codebases.