Business logic written inside a trigger
ID |
apex.maintainability.avoid_logic_trigger |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Api Design |
Language |
Apex |
Description
Reports control flow, DML or a query written directly in a .trigger file instead of in a handler class the trigger delegates to. The context router - an if on a Trigger context variable whose branches call a handler - is the recommended shape and is not reported.
Rationale
A trigger body is the one piece of Apex that cannot be unit-tested in isolation, cannot be reused, and cannot be ordered against the other triggers on the same object - the platform decides that. Logic that lives there is also invisible to the class-level tooling a team already uses: it has no interface, no constructor to inject a mock into, and no way to be called from a batch that needs the same behaviour. Once two triggers on one object both carry logic, the execution order between them is undefined.
The following code illustrates the pattern detected by this rule:
trigger AccountTrigger on Account (before insert, before update) {
if (Trigger.isBefore) {
AccountTriggerHandler.handleBefore(Trigger.new, Trigger.oldMap);
} else {
AccountTriggerHandler.handleAfter(Trigger.new, Trigger.oldMap);
}
// FLAGGED: Business logic written inside a trigger
if (Trigger.new.size() > 200) {
AccountTriggerHandler.warnOnBulk();
}
// FLAGGED: Business logic written inside a trigger
for (Account a : Trigger.new) {
a.Name = a.Name.trim();
}
// FLAGGED: Business logic written inside a trigger
insert new Task(Subject = 'follow up');
if (Trigger.isBefore && Trigger.isInsert) {
AccountTriggerHandler.handleBeforeInsert(Trigger.new);
} else if (Trigger.isAfter && Trigger.isUpdate) {
AccountTriggerHandler.handleAfterUpdate(Trigger.new, Trigger.oldMap);
}
}
Remediation
Move the body into a handler class and leave one delegation call in the trigger, e.g. AccountTriggerHandler.run(Trigger.operationType, Trigger.new, Trigger.oldMap);. The handler is then a normal class: testable, reusable from a batch, and explicit about the order in which each step runs.