Trigger Best Practices – Updating Multiple Related Objects

I have a question relating to the best practices when coding a trigger.

I have already read up a fair bit on the best practices and understand the one trigger per object pattern, as well as the helper class pattern to move logic away from the trigger.

My question is – what should we do when we have multiple actions for an object where each of these actions affects multiple other objects?

Using Case, Account and a custom object OBJECTABC.

OBJECTABC has a reference to Account but not to Case. There can be many OBJECTABC per Account.

We have the following two business rules that need to be implemented using a trigger:


Whenever a Case of type “FEEDBACK” is inserted or updated, it should also set the field “LastCaseUpdated” on the cases Account to the date that the Case was updated. It should also set the field “LastCaseUpdated” on OBJECTABC.


Whenever a Case is inserted and it is of type “ENQUIRY”, it should also increase the field “NumberOfCases” on the cases Account by one. It should also increase the field “NumberOfCases” on OBJECTABC.


I would like to retrieve the Account and the OBJECTABC records only once from the database using SOQL to reduce the number of SOQL calls. I would also like to only call UPDATE once for each of these objects.

Would the best practice be to have one handler class for Case, with methods for “SetLastUpdated” and “IncreaseCaseCount”? Would I then have collections for Account and OBJECTABC that I ensure are only loaded on the first trigger call, and then subsequent calls affect the collections instead of retrieving the data again? Issues I see with this is that it could become a very, very large messy handler class.

Would the best practice be to have separate handler classes for Account and OBJECTABC, each with their own methods for “SetLastUpdated” and “IncreaseCaseCount”? This would then mean duplicating the logic for each object (e.g. I’d need to check the case type for each of these in my example, but the logic that would need to be duplicated could be much more complex).

Would the best practice be to have separate handler classes for “SetLastUpdated” and “IncreaseCaseCount”? I could then keep the logic contained, but then how would I handle loading the Account and OBJECTABC only once, and how would I pass this around from handler to handler so that I only retrieve and update the objects once?

Apologies for the long question, and I look forward to your assistance 🙂


Here’s my opinion on what “best practice” is for trigger handlers.

  • Your Trigger should only answer when questions.
    • What event is firing?
  • Your Handler should only answer what/which questions.
    • What operation(s) to perform?
    • Which records to act on (criteria)?
  • Your Service should only answer how questions.
    • How to perform each action?
    • How to apply each filter (criterion)?

Each layer will pass stateful information (the trigger records) down as needed. In my observation, the term Helper class is not used rigorously as these terms above.

In your scenario, all your logic can be written in a CaseService class. Something like the following:

public with sharing class CaseService
    public static void updateChildAccounts(List<Case> records)
        Set<Id> accountIds = new Set<Id>();
        for (Case record : cases) accountIds.add(record.AccountId);

        List<SObject> recordsToUpdate = new List<SObject>();
        for (Account child : [
            SELECT Id, (SELECT Id FROM ABCs__r) FROM Account
            WHERE Id IN :accountIds
            // set account fields

            for (ABC__c grandchild : child.ABCs__r)
                // set ABC__c fields

        update recordsToUpdate;
        // error handling strongly recommended, but omitted here for brevity

There’s a lot to unpack about how the above was written to consume just one query and one dml operation. About the query limits, you did consume a second query by getting the children, but this type of sub-query consumes a separate governor limit. Usually this “aggregate query” limit is not one you have to worry about overly much.

The next thing to understand is that you can Create Records for Different Object Types. You can insert up to ten different types, but if you alternate back and forth between Account and ABC__c, each chunk counts towards that maximum. That’s where sort comes in, and thankfully the first step in the sort sequence is to check the type of sObject. So after you call sort you’re back down to two chunks and you’re good to go!

As for exception handling, you should read up on how to best handle a DmlException. I’m having a surprisingly hard time finding any good resources to link at the moment, but I’ll try to come back and add it in if I find one.

My basic pattern for the rest would look like:


trigger CaseTrigger on Case (before insert)
    CaseTriggerHandler handle = new CaseTriggerHandler(, trigger.oldMap);
    if (trigger.isBefore)
        if (trigger.isInsert) handle.beforeInsert();
        if (trigger.isUpdate) handle.beforeUpdate(); // if you needed it
    if (trigger.isAfter) // if you needed it
        // etc.


public with sharing class CaseTriggerHandler
    @TestVisible static Boolean bypassTrigger = false;
    final List<Case> newRecords;
    final Map<Id, Case> oldMap;
    public CaseTriggerHandler(List<Case> newRecords, Map<Id, Case> oldMap)
        this.newRecords = newRecords;
        this.oldMap = oldMap;

    public void beforeInsert()
        if (bypassTrigger) return;

    public void afterInsert() { /*if needed*/ }

    public void beforeUpdate() { /*if needed*/ }
    // etc.

Source : Link , Question Author : Darren Cann , Answer Author : Community

Leave a Comment