Apex Architecture
Last Updated on Tuesday, 8 September 2009 09:55 Written by Steve Tuesday, 8 September 2009 09:55
My post on Friday spawned some great discussion, even over the Labor Day weekend. If you were off enjoying summer’s last gasp, go back and check it out. In this post, I want to follow up on that and try to take the Apex architecture discussion to the next level of depth.
As I’ve said many times on this blog, I’m not a computer scientist, so I’m backing into architecture and often the concepts from CS-101 are new to me. I know I’m not the only one, so I’d like to make explicit some of the assumptions that top-notch developers are making every day. I surely am not an expert, and love to have developers set me straight–life is about continuous learning.
I’m going to lay things out as I understand them–if you think I’m making poor assumptions, please comment and set me straight. Also know that one of the reasons I love coding is that it is a creative act. It’s an amazing combination of a highly analytical process and one of inspiration and experimentation. I bet most programmers love that aspect of the job, and so I don’t believe there is a right way to do things, and I hope this may help to unleash creativity, rather than be seen as laying out any kind of Apex dogma that must be followed for fear of wearing the scarlet ‘no software’ mark on your breast.
#1: How hard should you work to have your triggers be absolutely minimal?
I’ve written a bunch of Apex code and also gotten to maintain Apex code written by others. I think writing software for maintainability should be one of the top goals of all coders. I think the most maintainable way to write Apex triggers is to have as little logic in them as possible. If you are trying to figure out how some code works, or doesn’t work, it’s a lot easier to know that the trigger can safely be ignored because it’s only job is to call the class methods that get things done.
But how hard should we try to do that? Should we make sure our trigger has no logic in it at all? If so, it could look something like this:
trigger MyTrigger on MyObject__c (before insert, before update, after insert, after update) {
MyObjectClass triggerSet = new MyObjectClass(Trigger.new, Trigger.old, Trigger.isBefore, Trigger.Context);
triggerSet.process();
}
This signature would only have to change if we decide to have this trigger fire in different contexts, like UNDELETE. Of course, it’s not possible now, because there is no Trigger.Context to pass the enum value of the DML context. But, if Jon gets his way and this enum value is created, we have minimized the trigger to 2 lines of code.
But I think there is an efficiency hit. We’re going to instantiate the MyObjectClass no matter what the trigger set includes. If we only want to process records when a certain value changes, wouldn’t it be more efficient to move some logic to the trigger, and only instantiate the object if some of the records in the trigger set need to be processed?
Or is it a negligible difference we shouldn’t worry about?
#2: When should we use static methods vs. object-oriented design?
The trigger above is object-oriented design where an object is instantiated and variables and methods exist for that instance of the object. But the trigger could be written this way too:
trigger MyTrigger on MyObject__c (before insert, before update, after insert, after update) {
boolean success = MyObjectClass.invoke(Trigger.new, Trigger.old, Trigger.isBefore, Trigger.Context);
}
So which is better? That’s something I’ve never been clear on–George made some great comments on my previous post along these lines.
The static method approach seems to make sense when your methods are straight-forward and you aren’t trying to maintain context across a number of actions. I like static methods for “utility” type functions:
utilities.getRecordTypeName(Id recordTypeId);
utilities.submitMyObjectForApproval(List
I personally like the object-oriented approach when invoking a trigger. I think it makes sense to instantiate the object that is going to handle the trigger set, and then kick that off with various methods based on the context and timing of the invocation. It seems to me that writing the code to collect all the data from the trigger is best written once, in the constructor. Here’s some pseudocode for a such a class:
public class MyObjectClass {
public MyObjectClass()
{
}
public boolean triggerIsBefore;
public enum context {Insert, Update, Insert, Upsert}
public context contextPassedFromTrigger;
public List<MyObject__c]]> triggerOldSet = new List<MyObject__c]]>();
public List<MyObject__c]]> triggerNewSet = new List<MyObject__c]]>();
public MyObjectClass(MyObject__c[] myObjects, MyObject__c[] myOldObjects, boolean isBefore, context triggerContext)
{
triggerIsBefore = isBefore;
contextPassedFromTrigger = triggerContext;
triggerOldSet = myOldObjects;
triggerNewSet = myObjects;
}
public process()
}
// BEFORE INSERT
if (contextPassedFromTrigger==context.Insert&&isBefore)
{
someBeforeInsertMethod()
}
{
}
The main difference between static and object-oriented functionality is that you can have multiple independent instances of an object, but not of a static variable. So, if you wanted to deal with 4 different opportunities at once, and maintain data or variables for each and then do some processing, object-oriented is probably the way to go.
Each trigger invocation is it’s own universe. scope-wise, so you don’t have to worry about static invocations conflicting across trigger invocations.
#3: What should go in a static Utils class?
I like object oriented design, and I always compliment that with static classes for “utility” type methods and variables. I think each module of functionality you write should have it’s own utility class that services the other classes you’ve got in that module. I gave the example of the getRecordTypeName() method. Also, all your references to Custom Labels and other strings can be placed in this Utils class, and then they can be accessed statically from your other methods.
#4: What are the limits of class proliferation?
This is a tough one. Let’s say I’ve got some complex functionality. It wouldn’t be at all out of the realm of possibility to have the following:
MyProcess.trigger – my trigger to handle DMLs
MyProcessClass – my main class for handling the trigger
MyProcessUtils – my utils class supporting all this functionality
MyProcessException – a class to extend the exception class for this functionality
MyProcessTest – tests supporting all this code
Other classes for functions that should be instantiatable on their own
VisualForce controllers for any necessary UI
With complex functionality it can get to be a lot of files. So how is this balanced with maintainability? Aren’t more files and more dependencies harder for a maintainer to track down?
It is true that the more classes you’ve got, the harder it will be for someone to find everything and get their head around it. Naming conventions are key to finding related functionality. Everything I write that is related to this process will be started with ‘MyProcess’. Things can get confusing where processes meet and overlap–there isn’t anything we can do about that other than document things well with process maps and other tools.
I think that while there may be a lot of classes in this architecture, and findability my suffer, changability can be enhanced. By encapsulating key functions in their own classes or in well-written methods in existing classes, changes can be more easily made. One thing I’ve learned about writing code is to ‘encapsulate that which is likely to change.’ In the short-term that may seem like a lot of infrastructure for relatively simple code, but when it needs to change it may be a lot easier.
So my main takeaways from this foray into thinking about Apex architecture are:
- Move most of your logic out of Apex Triggers and into Apex classes. Don’t stress out if your Triggers have some logic in them, but the less the better in most cases
- Decide where you fit on the spectrum between everything static and everything object oriented, and then be consistent. Maintainability will suffer if you’re changing up all the time for similar functions
- Use a static utilities class, and be mindful about what goes in there
- Don’t stress out about class proliferation as long as your doing it to encapsulate that which may change later, and aren’t forgetting about making things finadable
Excellent, as always. Thanks for your thoughts.
I started thinking about moving logic out of triggers a while back, but haven’t assembled the energy or time to set it off. This post had me thinking again. =)
Thanks Johan! I’ve been moving away from logic-heavy triggers for quite a while. Refactoring a working code base is always low on the priority list!
I haven’t yet had a real-world need for a Trigger that handles so many states, so a minimalist design was usually built-in.
What is a common use case that forces this level of refactoring? I’m guessing it’s Opportunity management (?)
-Mike
My most complex use cases have had to do with Opportunities and related processes. In my old gig we had to do custom rollups, so to get them right you really have to catch all cases.
It seems whenever revenue is involved, things get complex quick.
Another good post. Maybe dreamforce needs an apex/visual force design patterns session with input from the community as well as sfdc folks. A couple quick thoughts.
I don’t go quite as minimal as you are suggesting in the triggers I write. I mostly pull logic out of trigger and into a class or static method to make it easier to test, reuse code, or enhance code “readability”. But often testing is the main consideration. It is sometimes easier to get complete code coverage by attacking individual functions rather than testing at the trigger level.
The fourth question, class proliferation is a real problem in Apex, and something salesforce should address asap. They really need a proper package/folder hierarchy to organize classes. Sometimes I will use nested classes to get some form of encapsulation to keep related classes “grouped” together rather than place a lot of classes at the “root” level. But this does have its own challenges as well.
Thanks George! I know the product folks have some killer Dreamforce sessions on tap addressing design and architecture. It should be great as always.
I’m guessing “class proliferation” is related to “atomicity” in OO design, and most complex domains need to be atomic (ie lot’s of classes) to be maintainable.
I use the 500 line rule to determine when a class should be refactored into 2 (or 3).
In addition to George’s comment, I would also add the (unspeakable) suggestion of relaxing unit test coverage. Not all objects serve a mission critical purpose or modify persisted data.
Just stumbled across this blog and read the last 2 posts – interesting stuff! Adding you to my iGoogle page
I largely agree with your summary at the end. I actually find a lot of my headaches come from managing the lifecycle of workflows and triggers. Trigger fires and makes updates, workflow fires on updates which causes the trigger to fire again. I plan on blogging on this stuff myself but busy winning work right now, but if there’s material already out there, I’d welcome any pointers!