Blog

NPSP Table Driven Trigger Management (TDTM)

Beginning with the upcoming NPSP Release 3.0 (aka Cumulus), all trigger functionality is controlled through TDTM. As defined in the hub article. Be sure to review this article and watch the video on youtube:

Table Driven Trigger Management is a new method for managing the behavior of your Nonprofit Starter Pack code. Instead of each piece of functionality being driven by its own trigger, there is only a single trigger on each object. That trigger then calls a database table that provide a list of code to run in conjunction with the trigger action.

Prior to TDTM, the order was largely determined based on various classes calling each other, creating dependencies between those classes, and generally difficult-to-read code. Now those operations happen independently, and any associated database action (DML) are held until all actions are complete, if possible. This has the effect of speeding up your Nonprofit Starter Pack behavior. In addition, if you wanted to add any custom functionality that depended on these actions occurring, for example, creating a child object on the Opportunity based on the Contact Roles, you had no way of ensuring your code would run in the proper order. With TDTM, you can now implement the common interface, and add your custom code to the TDTM table in the settings panel, and define the order you want your code to be called in.

While migrating a client to the new version of the NPSP, I set out to see what TDTM could do. Before delving into the code, here are some pros and cons to using TDTM for your triggers:

Pros:

Obviously, your custom code will work and play quite well with NPSP Triggers

Provides superior control over the execution order of your code, both relative to NPSP Triggers and relative to your own customizations. Anyone who has followed the single-trigger-per-object pattern in the past will be familiar with this already.

Relatively straight forward to implement

Cons:

TDTM is only available for a small list of objects: Account, Contact, CampaignMember, Opportunity, and the key NPSP objects (Address, Relationship, Affiliation, Household, RecurringDonation). As of this blog post, it is not possible to extend the use of TDTM to any other objects. There is an open story on Github for this here.

Apex Unit Tests that do not use SeeAllData=true will not run your custom TDTM Trigger code unless you recreate the TDTM records prior to creating your test records. The packaged TDTM triggers will run even with SeeAllData=false. In my sample code below, recreating the custom trigger handler references is handled through the custom InitTDTM() method. There is an open story on Github for this here.

Any custom TDTM Trigger records created in Production will not be copied to a new or refreshed Sandbox and must be recreated in that org. As with above, any NPSP standard Trigger Handlers are automatically recreated in the Sandbox either by visiting the Settings page, or the first time you create a record for one of the objects Cumulus interacts with.

Custom apex code errors are masked and logged to the NPSP Error Log. This prevents your users from getting long apex error messages due to code problems, however it also complicates the debugging process. In particular, testing of code logic through Unit Tests (though this is best practice) are affected here because the code just doesn’t run with no visible explanation. System.Assert statements are key to identifying where a problem may exist, however debugging the exact root cause may be a challenge.

Implementing Custom TDTM Triggers

There various possible design patterns to follow with TDTM, such as:

A single TDTM Handler Class that controls flow for all objects, or at least the objects that the TDM is capable of handling.

One TDTM Handler Class per object.

For the purposes of this writing, we’ll stick with option 1. It’s easy enough to create split this out into one class per object. For simplicity, I’m using a second class to hold all the business logic (methods) called via the TDTM Trigger handler though that class is not included here as it’s not necessary to illustrate the overall usage of TDTM. Depending on the complexity of your implementation, those methods could be separated out into a single class per object.

The sample class below is enough to get you started with TDTM. The class calls a second class (TDTM_Processes) where the actual business logic is located. Your actual implementation can vary as appropriate based on org standards, business processes, etc.. For example, this class could call multiple other classes, such as TDTM_Account_Trigger and TDTM_Contact_Trigger.

Right now TDTM in NPSP does not support custom objects, however that functionality was added to the HEDA product last year. I recommend that you post this question in The Power of Us Hub in the NPSP Developers Group. You can @ mention me in your post.