Charge it, point it, zoom it, press it,
Write it, cut it, paste it, save it,
Load it, check it, quick – rewrite it,
Plug it, play it, burn it, rip it,
Drag and drop it, zip – unzip it,
Lock it, fill it, call it, find it,
View it, code it, jam – unlock it — Daft Punk’s Technologic.
(Hair) Triggers.
If you were to ask your project manager, and a developer to define a trigger, you’d probably end up with three very different answers. Often, Triggers are a quick-fix for project mangers who know the declarative interface just won’t solve this one. Raise your hand if you’ve ever heard the phrase “just a quick trigger”? Sometimes. Sometimes, triggers are just that, a quick-fix. But if you ask a Developers, you might hear those Daft Punk lyrics chanted in monotone. “Write it, cut it, paste it, save it, Load it, check it, quick – rewrite it” Sooner, rather than later, Developers learn first hand the rabbit hole that triggers can be. After all, what *kind* of trigger is asked for? …is really needed? How will adding this trigger affect the other triggers already in place? How will existing workflow and validation rules play into the trigger? Will the trigger cause problems with future workflows?
Triggers are phenomenally powerful, but that phenomenal power comes with phenomenal (potential) complexity. Awhile back, Kevin O’Hara a Force.com MVP from LevelEleven (They make some fantastic sales gamification software for Salesforce over at: http://leveleleven.com/) posted a framework for writing triggers that I like to call Triggers.new
Triggers.new
Kevin O’hara’s framework is based on a big architectural assumptions — Namely that your trigger logic doesn’t actually belong in your trigger; instead, your trigger logic lives in a dedicated class that is invoked by your trigger. Regardless of your adoption of this framework, placing your trigger logic in a dedicated class provides valuable structure to triggers in general and makes longterm maintainability much simpler. With this assumption in mind, the framework actually changes very little about how you write actual trigger file. Here’s a generic definition of the trigger utilizing the framework.
Trigger DescriptiveTriggerNameHere on ObjectNameHere (execution,context,list){ | |
new YourTriggerLogicClassNameHere().run(); | |
} |
Inside the logic class there are methods available to override from TriggerHandler that correspond to trigger execution states. i.e.: beforeInsert(). beforeUpdate(), beforeDelete(), afterInsert(), afterUpdate(), afterDelete(), and afterUndelete(). It’s inside these methods that your trigger logic actually resides. If, for Example, you wanted your ContactTrigger to apply some snark to your Contact’s Address your ContactTriggerLogic might look something like this:
Public Class ContactTriggerLogic Extends TriggerHandler { | |
Map<Id,Account> newMap; | |
Public ContactTriggerLogic() { | |
this.newMap = (Map<Id, Account>) Trigger.newMap; | |
} | |
Public Contact addSnark(Contact c){ | |
if(!c.lastName.contains(' is awesome!')){ | |
c.lastName = c.lastName + ' is awesome!'; | |
} | |
return c; | |
} | |
Public override void beforeInsert() { | |
for(Contact c : newMap.values()){ | |
addSnark(c); | |
} | |
} | |
//if your trigger definition included before update you could include | |
Public override void beforeUpdate() { | |
for(Contact c : newMap.values()){ | |
addSnark(c); | |
} | |
} | |
} |
So why do the extra work?
Not only does this framework help keep your code organized and clean, it also offers a couple of handy dandy, very nice(™) helpers along the way. As a trigger developer, you’ll sooner or later run into execution loops. An update fires your trigger, which updated related object B, which has trigger C which updates the original object … and we’re off. Kevin O’hara’s trigger framework has a built in trigger execution limit. Check it out:
Public Class ContactTriggerLogic Extends TriggerHandler { | |
Public ContactTriggerLogic() { | |
this.setMaxLoopCount(1); | |
} | |
} |
That bit of code: setMaxLoopCount(1), means that the second invocation of a given method i.e.: afterUpdate() within the same execution context will throw an error. Much less code than dealing with, and checking the state of, a static variable. Say it with me now: Very nice!
Perhaps even more important than the max invocation count helper, is the builtin bypass API. The bypass api allows you to selectively deactivate triggers programmatically, within your trigger code. Say what? Yeah, it took me a second to wrap my head around it to. Imagine the scenario: you’ve got a trigger on object A, which updates object B. Object B has it’s own set of triggers, and one or more of those triggers may update object A. Traditionally, your option for dealing with this has been just what we did above, use a setMaxIterationCount(), or a static variable to stop the trigger from executing multiple times. But with the bypass api we have new option; any trigger that is built with this framework can be bypassed thusly:
public override void afterUpdate() { | |
this.bypass('AccountTriggerHandler'); //yeah, if you could just ignore that accountTrigger, *that'd be great* | |
acc.Name = 'No Trigger'; // wait! where'd acc come from? … just keeping you on your toes. | |
update acc; // won't invoke the AccountTriggerHandler | |
this.clearBypass('AccountTriggerHandler'); // actually, yeah. need you run that accountTrigger. | |
acc.Name = 'With Trigger'; | |
update acc; // will invoke the AccountTriggerHandler | |
} //example lifted from github |
What’s next?
I believe that trigger frameworks like this one provide quite a few benefits over free-form triggers both in terms of raw features but also in terms of code quality. Splitting the logic out of the trigger and into a dedicated class generally increases testability, readability and structure. But this framework is just starting. Imagine the possibilities! What if you could provide your Admin with a visualforce page to enable or disable trigger execution? Wouldn’t that make your admin giggle and offer you Starbucks? #starbucksDrivenDevelopment