Skip to content

Class Name: TriggerHandler

Last Updated: 2025-10-22 Source Code: https://github.com/AANP-IT/I2C.Salesforce.Metadata/blob/STAGING/force-app/main/default/classes/TriggerHandler.cls

API Name: TriggerHandler Type: Utility (Framework/Base Class) Test Coverage: [To be verified via TriggerHandlerTest]

Business Purpose

The TriggerHandler class provides a standardized framework for implementing trigger logic across all Salesforce objects. This ensures consistent trigger structure, separation of concerns between triggers and business logic, testability through virtual methods, and support for all trigger contexts. This foundational framework class is used throughout the org's trigger architecture to maintain code quality and maintainability.

Class Overview

Scope and Sharing

  • Sharing Model: Not specified (inherits from context)
  • Access Modifier: public virtual
  • Interfaces Implemented: None

Key Responsibilities

  • Route trigger execution to appropriate handler methods based on trigger context
  • Provide virtual methods for all seven trigger contexts (before/after insert/update/delete/undelete)
  • Validate trigger execution context to prevent misuse outside of triggers
  • Support testing via @TestVisible triggerContext override
  • Provide extensibility point via andFinally() hook method

Public Methods

Constructor

protected TriggerHandler()

Purpose: Validates that handler is being used in appropriate context (within trigger or test).

Parameters: None

Returns: N/A (Constructor)

Throws: - TriggerHandlerException: When handler is instantiated outside of trigger context or test execution

Business Logic: Validates that either Trigger.isExecuting is true OR Test.isRunningTest() is true. If neither condition is met, throws an exception to prevent accidental misuse of the handler outside triggers.

Architecture Pattern Example:

Trigger File (e.g., AccountTrigger.trigger):

trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
    new AccountTriggerHandler().execute();
}


execute

public void execute()

Purpose: Routes trigger execution to the appropriate virtual method based on the current trigger operation context.

Parameters: None

Returns: void

Business Logic: Uses a switch statement to determine the current trigger operation context (with support for test override via triggerContext variable). Routes to the appropriate protected virtual method and then calls andFinally() hook for post-processing. Supports all seven trigger contexts: BEFORE_INSERT, BEFORE_UPDATE, BEFORE_DELETE, AFTER_INSERT, AFTER_UPDATE, AFTER_DELETE, and AFTER_UNDELETE.

Usage Example:

// In trigger file
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
    new AccountTriggerHandler().execute();
}

// In handler class
public class AccountTriggerHandler extends TriggerHandler {
    protected override void beforeInsert(List<SObject> newRecords) {
        // Custom logic for before insert
    }
}


Private/Helper Methods

Virtual Handler Methods

All handler methods below are protected virtual and meant to be overridden by subclasses:

beforeInsert

Purpose: Handle before insert logic for new records (records do not yet have IDs) Called By: execute() method when Trigger.operationType is BEFORE_INSERT

beforeUpdate

Purpose: Handle before update logic with access to both new and old values Called By: execute() method when Trigger.operationType is BEFORE_UPDATE

beforeDelete

Purpose: Handle before delete logic (can prevent deletion or perform logging) Called By: execute() method when Trigger.operationType is BEFORE_DELETE

afterInsert

Purpose: Handle after insert logic (records now have IDs, can create related records) Called By: execute() method when Trigger.operationType is AFTER_INSERT

afterUpdate

Purpose: Handle after update logic with access to both new and old values Called By: execute() method when Trigger.operationType is AFTER_UPDATE

afterDelete

Purpose: Handle after delete logic (cleanup, archiving, etc.) Called By: execute() method when Trigger.operationType is AFTER_DELETE

afterUndelete

Purpose: Handle after undelete logic (restore relationships, etc.) Called By: execute() method when Trigger.operationType is AFTER_UNDELETE

andFinally

Purpose: Runs after every trigger context to provide post-processing hook Called By: execute() method after routing to specific handler Use Cases: Apex Rollup pattern, logging, auditing, cleanup operations

TriggerHandlerException (Inner Class)

Purpose: Custom exception for framework errors Called By: Constructor when validation fails


Dependencies

Apex Classes

  • None (base class - does not depend on other custom classes)

Salesforce Objects

  • All SObjects (generic framework supports any object type)

Custom Settings/Metadata

  • None

External Services

  • None

Design Patterns

  • Template Method Pattern: Base class defines algorithm structure (execute()), subclasses implement specific steps
  • Virtual Methods: All handler methods are virtual to allow selective overriding by subclasses
  • Single Trigger Pattern: One trigger file per object, all logic delegated to handler
  • Separation of Concerns: Trigger files only instantiate and execute handlers
  • Hook Method Pattern: andFinally() provides extensibility point for post-processing

Governor Limits Considerations

SOQL Queries: N/A - Framework class does not execute queries DML Operations: N/A - Framework class does not perform DML CPU Time: Minimal - Simple routing logic only Heap Size: Minimal - No data collection or caching

Bulkification: Framework supports bulk patterns through List and Map parameters Async Processing: Not applicable - synchronous routing only

Error Handling

Strategy: Constructor validation with custom exception Logging: No built-in logging (left to subclass implementations) User Notifications: Exceptions propagate to trigger context for standard error handling

Security Considerations

Sharing Rules: Not specified - inherits from subclass implementation Field-Level Security: Not applicable - framework does not access fields CRUD Permissions: Not applicable - framework does not perform DML Input Validation: Validates execution context in constructor

Test Class

Test Class: TriggerHandlerTest.cls Coverage: [To be verified] Test Scenarios Covered: - Valid trigger context execution for all seven contexts - Invalid context validation (outside trigger) - Virtual method overriding - andFinally() hook execution - Test context override functionality

Changes & History

  • 2024-12-20: Initial implementation by Ryan O'Sullivan - Trigger framework foundation

⚠️ Pre-Go-Live Concerns

CRITICAL - Fix Before Go-Live

  • None identified

HIGH - Address Soon After Go-Live

  • None identified

MEDIUM - Future Enhancement

  • No Recursion Control: Framework doesn't prevent infinite loops when triggers call DML that fires same trigger
  • Test.isRunningTest() Anti-pattern: Constructor uses Test.isRunningTest() which can cause code to behave differently in tests vs production
  • @TestVisible on TriggerHandlerException: Unnecessary annotation (exceptions are accessible in tests by default)

LOW - Monitor

  • No Context Tracking: Doesn't track which handlers have executed in current transaction
  • triggerContext Test Override: While useful for testing, breaks encapsulation slightly

Maintenance Notes

Complexity: Low Recommended Review Schedule: Annually (stable framework class) Key Maintainer Notes: - This is a foundational framework class used by all trigger handlers - changes should be rare and carefully reviewed - Subclasses must not modify core routing logic - only override virtual methods - When adding new trigger handlers, ensure they extend this base class for consistency - Test context override (triggerContext variable) is for testing only - never use in production code - The andFinally() hook executes after every trigger context - be mindful of cumulative processing costs