If your main concern is cleaning up your domain layer, you can take a look at Apex Enterprise Patterns: Domain & Selector Layers . I have a slightly different convention but any move to a cleaner separation of domain layer will likely help. My preferred pattern looks more like the below, though the idea is much the same.
public trigger Opportunity on Opportunity (before insert)
{
OpportunityTriggerHandler handle = new OpportunityTriggerHandle(trigger.new, trigger.oldMap);
switch on trigger.operationType
{
when BEFORE_INSERT { handle.beforeInsert(); }
// other operations as needed
}
}
public class OpportunityTriggerHandler
{
@TestVisible static Boolean bypassTrigger = false;
final List<Opportunity> newRecords;
final Map<Id, Opportunity> oldMap;
public OpportunityTriggerHandler(List<Opportunity> newRecords, Map<Id, Opportunity> oldMap)
{
this.newRecords = newRecords;
this.oldMap = oldMap;
}
public void beforeInsert()
{
if (bypassTrigger) return;
SpecificOpportunitySubservice.doSomeOperation(
OpportunityFilters.meetsSomeCriteria(newRecords)
);
}
}
Basically, your trigger handler will handle which operations are bound to which events, and what filters to apply. But that class shouldn't tell you how to do anything, really, and we generally don't test it directly at all. It can be very thoroughly tested by testing the trigger itself, and any further testing on just the handler would be redundant.
Also, since you will be breaking logic down into separate filter and service methods, those smaller chunks should be where you do you most rigorous testing. The more you can avoid database interaction, the better efficiency you will get in protecting your code base, hitting as many cyclomatic nodes as possible with the least test run time.
Probably my biggest disagreement with the Apex Design Pattern of Domain Layer
is that I still put validations in their own service. I do not feel they share an overlapping concern with the Handler, which is our version of the Domain Layer.
It's also worth reading up about the Service Layer Pattern and thinking about the idea of Separation Of Concerns
as you approach the task of breaking your codebase into more manageable chunks.
Impl
.