This question was asked similarly last month in Break single responsibility to save a soql call and was closed for being opinion based. The given use case also involved Queueable and so wouldn't likely be faced with the same governor risk as a synchronous transaction.
However, as a question in general, I disagree that it's opinion based, so I'm going to try asking it again.
The Single Responsibility principle tells us that a class or method should only have one reason to change, and only one responsibility. Taking it a step further and looking at "Traditional" Trigger Frameworks vs Dynamic Metadata Trigger Frameworks, we can see that the more modern metadata trigger frameworks are following a decoupled single responsibility structure, where new features & functionality are introduced as new classes which run independently and are called dynamically from the trigger framework.
The most common governor limit challenges I can imagine in this structure are SOQL and Heap. Heap is manageable to a degree by collecting your own garbage after you are done working with data, but 100 SOQL queries is a hard limit.
I know that pre-mature optimization is the root of all evil, and I agree - but in the environment I've adopted there is a real reason to be concerned that we may have enough cascading apex workflows to brush by that limit.
Take for example the following Metadata trigger structure:
In this workflow, EventTrigger
is the sole trigger firing in all contexts. It calls the MetadataTriggerHandler
which pulls the metadata records from Metadata_Object__c
to understand what to run for this trigger, and then runs them in a predefined order in the correct contexts. Both of the classes, EventCreateAttendees
and EventCreateAffiliates
have a single responsibility of creating different SObject Types off the back of the Event insertion. However, both are reliant on Contact data pulled through the parent relationship query of WhoId
. Both need different fields returned in their queries.
In this design, we will consume 2 SOQL queries to collect all of this different information. The singleton pattern of creating a class/method to run and cache this query in a static variable doesn't work either because both EventCreateAttendees
and EventCreateAffiliates
might be querying for different Contact Ids based on Event filtering conditions defined in their codebase.
If we break the single responsibility pattern, we could create a single EventCreateRelatedRecords
class which loops through the full Trigger.new to find all Contact Ids at once, does one larger SOQL query for all of the combined fields for all matched Contacts, and then uses the fields appropriately to create both Attendees
and Affiliates
.
So I guess, ultimately, my longwinded question is...
Do we as architects just decide which governor limits we want to battle against more often and pick a design that fits 80% of our use cases? Do we break the generally accepted SOLID principles when necessary to solve for governor limits? Or is there an acceptable approach that solves for this type of issue while still being considered best practice and aligned with SOLID?