Tag Archives: Trigger

Salesforce Apex Trigger Events

  • Before Insert: Trigger that executes due to an insert operation and before the record is saved to the database it is a before Insert trigger
  • Before Update: Trigger that executes due to an update operation and before the record is saved to the database it is a before update trigger
  • Before Delete: Trigger that executes due to a delete operation and before the record is saved to the database it is a before delete trigger
  • After Insert: Trigger that executes due to an insert operation but after the record is saved to the database it is an after Insert trigger
  • After Update: Trigger that executes due to an update operation and after the record is saved to the database it is an after update trigger
  • After Delete: Trigger that executes due to a delete operation and after the record is deleted from the database it is an after delete trigger
  • After Undelete: Trigger that executes due to a undelete operation and after the record is undeleted from the database it is an after undelete trigger

An Introduction to Force.com Development

download

Salesforce is a SaaS (software-as-a-service), global enterprise software company best known for its Customer Relationship Management (CRM) that is now also offering platform-as-a-service. Salesforce is a pioneer in providing high quality on-demand CRM software (referred to as “The Sales cloud” by Salesforce). They have diversified into customer service and support on-demand offerings as well (referred to as “The service cloud” by Salesforce). Today the world is leaded by Cloud computing. Salesforce.com is one of the way for moving all your applications to cloud.
This article introduces the development on Force.com platform:

Sign Up for Developer Edition

  • In your browser, go to http://developer.force.com/join
  • Fill in the fields about you and your company.
  • In the Email Address field, make sure to use a public address you can easily check from a Web browser.
  • Type a unique Username. Note that this field is also in the form of an email address, but it does not have to be the same as your email address, and in fact, it’s usually better if they aren’t the same. Your username is your login and your identity on developer.force.com, so you’re often better served by choosing a username such as firstname@lastname.com or firstname@force.com.
  • Read and then select the checkbox for the Master Subscription Agreement. And then click Submit Registration.
  • In a moment you’ll receive an email with a login link. Click the link and change your password.

Object:

  • Object logically equal to database tables in a relational database. Fields of an object is similar in concept to column in relational database.
  • Object can be standard or custom. Standard are objects are predefined by Salesforce like Account, Contact, Lead, Case, Opportunity etc.
  • Custom objects are created by developers based on requirements of application.
  • Custom objects have the following characteristics:
    • Custom objects store information that is unique and important to your organization.
    • Custom objects are reportable and search-able.
    • Custom objects have configurable access control features.
  • Force.com provides un-delete functionality on objects. Deleted objects go to a recycle bin with an expiry date of 45 days. An administrator may delete objects from recycle bin.
  • The Object name is used to access the object programmatically. __c is added as a suffix to the custom object names. The label name is used for display of the object.
  • Object have following fields that need to be entered by developers:
    • Label
    • Plural Label
    • Object Name
    • Description
    • Record Name
    • Record Type
  • An Object can have up to 500 custom fields.
    When an object is created, a user interface is created automatically for Create, Update, Read and Delete operations.
  • Custom objects can be represented using a Metadata XML.
  • Deleted data and metadata is stored in recycle bin.
  • Database tuning is managed by Salesforce. How Objects and fields are stored in the database is internal to Salesforce.

Object Relationships:

  • Force.com allows to create relationship between objects. The “Custom Fields & Relationship” section of objects is used to define relationship between objects.
  • There are a two types of object relationships that Salesforce supports:
    • Lookup relationship
    • Master-detail relationship
  • These relationships are used to implement one-to-many relationship. They are created as fields in the child record. As an example in a Customer feedback if one customer can have many customer feedback, we could create a lookup (or master detail) relationship in the customer feedback object pointing to the applicant object.
  • In Master Detail relationship, if the parent record is deleted, then all its children records are deleted.
  • Child records in master-detail relationship do not have owners. They inherit ownership from the parent record.
  • In Master detail relationship, the parent field is required. Also the parent field once specified cannot be changed.
  • Standard Object cannot be on detail side of Master-Detail relationship.
  • In the related list section of parent objects, only one field of the child object is displayed by default. This is the field specified as Record Name in the child object. To add more fields, search lookup layout needs to be updated.
  • Rollup-summary fields are supported in master detail relationship. The parent object can use roll-up summary field type to perform operations of sum, maximum, minimum, count among its children records. These fields are read only fields and are used to calculate values from a set of records.
  • Many-to-Many relationships are implemented using two master-detail objects. One Junction object is used as the child of the objects between which many-to-many relationship needs to be established.
  • The table below compares Master Detail and Lookup relationship:
    Lookup Master-Detail
    Loosely coupled. Tightly coupled.
    Parent is not a required field while creating child record. Parent is a required field while creating child record.
    Cannot create roll-up summary field. Can create roll-up summary field.
    No cascade delete. Cascade delete.
  • Self-relationship is a lookup relationship to itself. An example usage could be organization chart where an employee’s manager is also an employee.
  • Hierarchy Relationship exists only for User object. In this, developers create a manager field in User object to relate to another object.

Fields:

  • Fields of type Picklist cannot be made required.
  • The field for record name is displayed by default when the object has to be displayed (for example in search results). The record type can be text or auto-number. Auto-number can take values like A-{0001}, A-{0002} etc.
  • The standard fields are added automatically. Examples of standard fields are Created By, Owner etc.
  • Custom Fields are added to an object by developers. In the custom object definition detail screen there is a section of custom fields. Click New to create new custom fields in this area.
  • Custom Fields have properties like:
    • Data Type
    • Field Label
    • Field Name
    • Required checkbox
    • Description
    • Default Value
  • The field types are aligned to user interface elements like picklist and checkbox.
  • Changing the data type of existing custom fields is possible, but doing so may cause data loss.
  • Fields can be set as unique, required or as external id. A required field is always displayed in the edit page. A field of type external id is a record id from another system. The performance of reports and SOQL is better for fields defined as external ids. Fields of type number, text and email can be set as external id. Each object can have up to three external ids.
  • A field defined as encrypted is not visible to users. Typical usage of encrypted field is for password. Only users with “View Encrypted Data” can view the encrypted fields. Encrypted fields are editable. This is a provisioned feature, so you must contact Salesforce to enable it.
  • Encrypted custom field cannot be unique, an external ID, or have default values.
  • Fields of two Picklists can be made dependent on each other. As an example consider an application with customers in UK and India. If there are two picklists – one for country and the other for state. Based upon user’s selections of country, the state settings need to get updated. This is implemented by defining controlling and dependent picklists. In the above scenario, country becomes the controlling picklist and state becomes dependent picklist. The controlling and dependent picklists are defined using “Field Dependency” button in “Custom Field and Relationship” section.
  • Standard picklist can be controlling picklist but not dependent picklist. Maximum number of values allowed in controlling field is 300. A custom multi-select picklist cannot be controlling field in a picklist.
  • Merge fields are fields that display values based upon formula calculations.
  • Salesforce supports history tracking on change in values of fields. This can be enabled for up to 20 fields in an object.

Page Layouts:

  • Page layouts and search layouts available in: Enterprise, Performance, Unlimited, and Developer Editions.
  • There are three type of page layouts:
    • Detail
    • Mini
    • Console

SOQL:

  • SOQL (Salesforce Object Query Language) statements are similar to the select statement in SQL and is case insensitive. It can be used in Apex as well as the Web Services API.
  • If you get a “consider and Index filter” error, it means that you are returning too may records and need to use a WHERE clause to restrict the records returned.
  • You can insert SOQL into Apex using square-brackets, such as:
    [SELECT Name FROM Position__c] 
  • Alternatively, you can use the Database.query method, such as:
    Database.query(‘SELECT ‘ + myFieldString + ‘FROM ACCOUNT’) 
  • Can return 4 data types:
    • List of sObjects – Used if statement returns multiple records.
    • Single sObject – Used if only one record is returned.
    • Integer – Result should be stored in variable of Integer data type.
    • List of Aggregate Results (when using GROUP BY)
  • SOQL Functions:
    • AVG()
    • COUNT()
    • COUNT_DISTINCT() – returns distinct non-null field values
    • MIN()
    • MAX()
    • SUM()
    • CALENDAR_MONTH() – returns a number, as do the ones below
    • DAY_IN_MONTH()
    • FISCAL_YEAR()
    • WEEK_IN_YEAR()
  • SOQL Binding: SQL Statements in square-brackets can reference an expression that is preceded by a colon. For example:
    String jobtype = ‘Full Time’;
    Position__c[] positions;
    Positions = [SELECT Name FROM Position__c WHERE Type__c =:jobtype]; 
    
  • SOQL Returned Data: Only returns data for fields that are specified and exceptions are raised if you try to reference a field that was not returned in the query.
  • A SOQL For loop can iterate through all sObject records returned.Two types supported:
    • Iterating over a single variable:
      For (Account a: [SELECT id, name FROM account WHERE name = ‘Biswajeet Samal’]){
      System.debug(a.name);
      }  
      
    • Iterating over a variable list: (processes records in blocks of 200 at a time)
      For(Account[] accts : [SELECT id, name FROM account WHERE name like ‘%Biswajeet%’]){
          For (Account acct: accts) {
              //Your code without DML statements
          }
          Database.update(accts);
      }  
      
  • SOQL Keywords:
    • IN – Used for bulk queries; Uses a Set or a List of sObjects as an argument.
    • LIKE
    • AND/OR
    • NOT – can negate in, like, and null keywords.
    • ORDER BY – sorts in ascending by default.
    • GROUP BY
    • LIMIT – Returns top record when used with Order By.
    • FOR UPDATE – Locks the returned record from being updated by other request.
    • ALL ROWS – returns both active and deleted records.
  • SOQL Queries – Different Types of Joins:
    • Right-Outer join. For example, to get job applications with associated position data:
       SELECT Name, Position__r.Dept__c FROM Job_Application__c
    • Left-Outer join. For example, to get positions and related job application data:
       SELECT Name, (SELECT Name FROM Job_Applications__r) FROM Position__c
    • Semi-Join. For example, to get only positions with related job applications:
      SELECT Name FROM Position__c WHERE Id IN (Select Position__c FROM Job_Application__c)
    • Right Inner join. For example, to get only job applications with related positions:
       SELECT Name, Status__c, Position__r.Name FROM Job_Application__c WHERE Position__c != null
    • Right Anti join. For example, to get only job applications without related positions:
       SELECT NAME, Status__c FROM Job_Application__c WHERE Position__c = null
    • Left Anti join. For example, to get only positions without related job applications:
       SELECT Name FROM Position__c WHERE Id NOT IN (SELECT Position__c FROM Job_Application__c)
    • Right Inner Join without Remote Condition. For example, to get only job applications with certain related positions:
      SELECT Name, Status__c, Position__r.Name FROM Job_Application__c WHERE Position__r.Dept__c = ‘Engineering’
  • Multi-Level Relationship: Relationship names can be chained together to access grandparents. You cannot specify more than 5 child-to-parent relationships and only one parent-to-child relationship.

SOSL:

  • SOSL (Salesforce Object Search Language) queries enable text searches across multiple sObjects and returns a list of lists of sObjects.
  • Returns all searchable fields (excluding dates, ids and numbers), by default.
  • Return only ID’s, by default. For example:
    List<list<sobject>> acmeObjects = * FIND ‘Acme’ RETURNING Account(Name), Opportunity(name)]; 
    
  • Statement specifies:
    • Text expression.
    • Scope of fields to search.
    • List of objects and fields to retrieve.
  • Conditions for selecting rows in the source objects.
  • The basic structure includes the FIND clause.
  • Clauses:
    • FIND – Specifies the search string surrounded by single quote. Can use * to do a wildcard search that matches one or more characters in the middle or the search string and ? to do a wildcard search that matches one character in the middle or at the end of the search string.
    • IN – (optional) limits the types of fields to search. If not specified, it will search all text in all objects. You could specify that it search only email, name, phone, or sidebar fields.
    • RETURNING – (optional) limits the results to specified object types. If not specified, it will return the ID’s of all objects that are searchable.
    • WHERE – (optional)
  • SOQL vs SOSL: SOQL is best when the sObject is predetermined and the result data needs to be joined. You should use SOSL when searches need to occur across multiple sObject types.
  • DML Operations:
    • Can take two forms, but is always restricted to one sObject at a time.
    • Standalone statements. For example:
      Account account = new Account(Name = ‘Universal Containers’);
      Insert account;
      
    • Database methods. For example:
      Account account = new Account(Name = ‘Universal Containers’);
      Database.insert(account);
      
  • Database methods (Insert and update) include an optional allOrNone parameter that defaults to true. They will both return a SaveResult object (even if a single record is submitted). If a list of records is submitted, a List list object is returned. The Delete method will return a DeleteResult object.
  • SaveResult object has 3 methods:
    • ID(getID)
    • List getErrors();
    • Boolean isSuccess()
  • Database. DMLOptions – allows you to include additional information, such as Truncation behavior, Locale options, and trigger assignment rules. Can also be used to trigger email notifications based on specific events, such as:
    • Creation of a new case or task.
    • Creation of a case comment.
    • Conversion of a case email to a contact.
    • New user email notification.
    • Password reset.
  • External ID’s can be used to establish foreign key relationships among sObjects. For example:
    Account refAcct = new Account(externalId__c = ‘12345’);
    Contact c = new Contact(account = refAcct, lastName = ‘kay’);
    Upsert c;
    
  • It is bad practice to execute dml operations or SOQL queries inside of for loops. For example, this is a better way of handling an update with a for loop:
    For(List<position__c> positionList :[SELECT id FROM Position__c]) {
           For(Position__c p: positionList) {
                            //code block
                            }
           Database.update(positionList);
           }
    

Apex SOQL and DML Governor Limits :

  • When governor limits are reached, you can consider using Apex asynchronous batch processing for DML operations.
  • If you exceed the total # of SOQL queries, move them outside of loops.
  • If you exceed the total # of executed script statements, reduce the number of loops.
  • If you exceed the # of DML statements, insert, update or delete records in bulk.
  • If you exceed the total # of records retrieved by SOQL queries, create selective queries that filter indexed fields, such as primary keys, foreign keys, name, audit dates, or external ID fields.

Database Transactions:

  • If stand-alone commands are used, the transaction always executes completely or does not execute at all.
  • If the method-based DML statements are used, the transaction level can be set through the allOrNothing parameter.
  • You can define savepoints to control the transaction level and define logic for rolling back a section or a subset of the DML operation.
  • Each setSavepoint and rollback counts against the total number of DML statements.

Triggers:

  • Executes code when a DML event occurs on a specific sObject.
  • Can be created on any custom or standard object.
  • Two types:
    • Before – can update or validate values before they are saved to the DB.
    • After
  • Syntax:
    Trigger <triggername> on <objectname> (<trigger_events>) {
                               //code block
                            }
    
  • Trigger Events:
    • Before insert
    • Before update
    • Before delete
    • After insert
    • After update
    • After delete
    • After undelete
  • To create triggers through the UI, you go to the object and select Triggers. You go through customize if it is a standard object and Create -> Objects if it is a custom object.
  • Trigger Execution Order:
    • Original record is loaded from the DB.
    • The new record field values are loaded and old values are overwritten.
    • System Validation rules are executed and required fields are verified.
    • All before triggers are executed.
    • Most system validation steps are run again and validation rules are checked.
    • The record is saved to the DB, but not committed.
    • All after triggers are executed.
    • Assignment rules, auto-response rules, and workflow rules are executed.
    • Records are updated again if workflow fields are updated.
    • Before and after triggers are executed again if fields are updated based on workflow rules.
    • Escalation rules are executed.
    • Rollup summary formula or cross-object formula fields are updated in the parent records and affected records are saved.
    • Repeat the same process for affected grandparent records.
    • Criteria based sharing evaluation is executed.
    • All DML operations are committed to the DB.
    • Post-commit logic is executed.
  • Types of Trigger Context Variables:
    • Trigger context Boolean Variables, such as Trigger.isBefore and Trigger.isInsert
    • Size – returns total # of new and old records.
    • New – returns a list of the new versions of the sObject records. But is used only for insert and update triggers and can be modified only when used with before triggers.
    • newMap – returns a map of ID’s for the new versions of sObject records and is used only for update, insert or afterUndelete triggers.
    • old – returns the current version of the records and is used only for update and delete triggers.
    • oldMap
    • You use the newMap and oldMap ID to sObject maps to correlate records with query results.
    • You use the addError method to prevent DML operations from taking action on specific records.
    • This is very useful for applying custom validation rules. It can be applied at the sObject or field level (which affects where the error message appears on the page). For example, the following code is used to prevent a user from deleting an opportunity with a quote:
      Trigger opptrigger on Opportunity (before delete) {
          For (Quote__c q : [Select opportunity__c from quote__c
          where opportunity__c in : Trigger.oldMap.keySet()]) {
              Trigger.oldMap.get(q.opportunity__c).addError(‘Cannot delete opportunity with a quote’);
              }
          }
      
    • Trigger.new and old cannot be used as part of a DML operation.
    • Trigger.old is always read-only.
    • Trigger.new cannot be deleted.
    • You can modify field values before they are writer to the DB by using Trigger.new in before triggers.
    • When trigger.new is used in after triggers, it is not saved and an exception is thrown.
    • Trigger.new[0] processes only the first record in a multi-record batch
  • Trigger Considerations:
    • Since all trigger can execute through API access, you have to assume it can process multiple records at a time.
    • Trigger code cannot contain the static keyword and can only contain keywords applicable to an inner class.
    • Cascading execution of triggers are part of the same execution context for governor limits.
    • Upsert events cause both the insert and update triggers to fire.
    • Merge events execute delete triggers for the unsuccessful records and update triggers for the successful records.
    • The after undelete trigger works only with recovered records.
    • Undelete events run only on top-level objects.

Apex Code:
Force.com Apex Code is a strongly-typed programming language that executes on the Force.com platform. Apex is used to add business logic to applications, to write database triggers, and to program controllers in the user interface layer. It has a tight integration with the database and query language, good web services support, and includes features such as futures and governors for execution in a multi-tenant environment.
Classes :

  • A class is a template or blueprint from which objects are created.
  • They consist of methods and attributes.
  • Are stored with the version of API that is used to compile it.
  • May contain other classes, known as inner classes (but these can only be one level deep).
  • Even though Apex code is not case sensitive, it is recommended that you follow the Java naming convention.
  • Static methods and attributes can only be declared in a top-level class definition.
  • To create new exception classes, the Exception class must be extended.
  • Classes can be enabled or disabled for profiles and can only be 100,000 characters in length.
  • Ways to Create Classes:
    • Through the UI:
      • Go to Setup -> Develop and Apex Classes.
      • Click “New” or “Generate from WSDL”.
      • Enter code or upload a WSDL
    • Through a Force.com IDE project:
      • Right-click the src folder and click New -> Apex Class (this is the recommended way)

Class Syntax:

private | public | global
 
[virtual  | abstract | with sharing | without sharing | (none)]
 
Class ClassName [implements InterfaceNameList | (none)] [extends ClassOrInterfaceName | (none}] {
 
// The body of the class
}
  • If global, the class is accessible everywhere. This must be used with the webService keyword. All methods, attributes and inner classes that are global must be within a global class.
  • If public, the class is visible across the application, org or namespace that comprises the class.
  • If private is used for an inner class, the inner class is only accessible to the outer class. The default for inner classes is private.
  • Top level or outer classes must have either a global or public keyword.
  • A class can implement multiple interfaces, but it can only extend once class.
  • A class can cast as a superclass and verify an objects class using the instanceof keyword.
  • You can implement and extend classes using the keywords, virtual, abstract and extends.
    • Virtual declares that the class allows extensions and overrides, Classes that are virtual cannot be global.
    • Abstract declares that the class contains abstract methods and can be extended. These classes just have a method signature and do not have code. You cannot instantiate an object of an abstract class until some other class extends it.
    • The Extends keyword declares that the class is a subclass. The “super” keyword can be used to invoke constructors and methods from the parent class.
  • All Apex code executes in the system mode and ignores all CRUD, field-level security and record sharing level privileges. You use with and without sharing keywords to implement sharing. By default a class will run in the without sharing mode.
    • With sharing – This means that when performing DML operations, the user can only update records to which he or she has edit level access.
    • Without Sharing – This ensures that sharing model access is ignored and is referred to a running in system mode.
  • Interfaces are classes that only include the method signature. The methods are not implemented in the interfaces. Apex supports both top-level and inner interfaces

Attributes Syntax:
modifiers dataType attributeName initialization;
For example:

public static final Integer MAX_AMOUNT = 200;
 
Methods Syntax :
 
Modifiers returnDataType methodName (inputParameterList) {
// method code
}

For example:

Public Integer getInt() {
Return myInt;
}

Method and Attribute Access Modifiers :

  • Private – Default access modifier, but is not available for top-level classes. It implies that the attribute or method is only available in the class where it is defined.
  • Protected – Available only to inner classes.
  • Public – Can be used by an apex code in the application, org or namespace.
  • Global – Accessible by all Apex everywhere. Note that Apex code cannot be shared between orgs, with the exception of Web Services, which are accessible to everyone.

Static Methods and Attributes :

  • Static methods are accessed through the class itself and do not depend on an instance of a class.
  • Static attributes are used to store data that is used within the class. They can be used to prevent recursive logic by setting flags.

Constants:

  • Assign a value to a constant only once either at declaration or initialization.
  • Define a constant using both the static and final keywords.

Instantiating Objects:

  • Allows you to work with methods and attributes that are not defined as static.
  • After instantiating, you can refer to methods and attributes using the dot notation. For example:
    TestObject myObject = new TextObject();
    myObject.myMethod();
    
  • A constructor is a special method used to create an object of a class. It has the same name as the class and is the first method invoked in the class. It does not have an explicit return type and is available by default in each class as invisible and without parameters. It can be overloaded by defining multiple constructors with different parameters.
  • The “this” Keyword:
    • With dot Notation – Used to represent the current instance of the class and can call methods or set attributes that are public or are available.
    • With Constructors – Developers can do constructor chaining by using the this keyword, but the this keyword must be the first statement in the constructor.

Apex System-Delivered Classes:

  • System Class – Is a static class that contains only static methods. Includes the following:
    • Debug()
    • Now()
    • Today()
    • Assert()
    • AssertEquals()
    • AssertNotEquals()
  • UserInfo Class – Mostly getter methods used to get details. For example:
    • getUserId()
    • getUserName()
    • getUserRoleId()
    • getFirstName()
    • getLocale()
    • getLanguage()

Limit Methods:

Can be used for debugging purposes. There are two versions of each built-in method. The first version returns the amount of resource being used and the second returns the total amount of the resource available. For example:

  • getDMLRows()
  • getDMLStatements()
  • getHeapSize()
  • getQueries()
  • getQueryRows()
  • getScriptstatements()

Apex Workflow and Approval Processing:
Apex process classes are used to submit workflow requests and process the results of those requests. The three classes provided are:

  • ProcessRequest – Used to process the results from a workflow process.
  • ProcessSubmitRequest – Used to submit a workflow item for approval.
  • ProcessWorkItemRequest – Used to process an item after it is submitted.

Usage of Trigger.IsExecuting in Salesforce

Trigger.isExecuting is to determine that your current context for the Apex code is a trigger, not a Visualforce page, a Web service, or an executeanonymous() API call.
Here is an example of Apex Class, this class can identify current context for the Apex code is a trigger using Trigger.isExecuting.

Apex Class:

public class AccountHandler
{
    public Boolean handleAccount(List<Account> accList){
        
        System.debug('Trigger is executing : ' + Trigger.isExecuting);
        
        if(Trigger.isExecuting){
			//Do what ever you want to do as part of the trigger invocation
        }
        else{
            //Do what ever you want to do if the call originated from different context, such as from controller.
        }
        return Trigger.isExecuting;
    }
}

Apex Trigger:

trigger AccountTrigger on Account (before insert, before update){
    AccountHandler handler = new AccountHandler();
    handler.handleAccount(Trigger.New);
}

How to deploy any trigger from sandbox to production?

In order to deploy a trigger from sandbox to production, you need to take care of some points:

  • At first I would like to request you all that before going for any deployment, make sure that there is no test failure or less than 75% code coverage in Production Instance. Because when we deploy any thing from sandbox to Production, “Run All Test” is performed automatically in Production. If there is any issue in Production then it will not let you deploy your new code.
  • If you are deploying any trigger in Production, then you must write a test class for that trigger with no Test failure and more than 75% code coverage.
  • Create deployment connection in sandbox.
  • Create Outbound change set in Sandbox and add your trigger and its associated test class in the change set.
  • Create Inbound change set in Production.
  • Add Change set components in Sandbox.
  • Upload the change set.
  • You can validate the change set in Production.
  • Finally deploy the change set in Production.

Hope this will help to deploy a trigger from sandbox to production.

Salesforce Trigger Handler Patterns

To move all code from Trigger to Apex Classes(Handler Classes) is one of the Salesforce Best Practices. Here is an example to write Trigger Handler class.

Apex Trigger:

trigger objectTrigger on Object (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
    
    objectTriggerHandler handler = new objectTriggerHandler();
    
    //Before Insert
    if(Trigger.isInsert && Trigger.isBefore){
        handler.OnBeforeInsert(Trigger.new);
    }
    //After Insert
    else if(Trigger.isInsert && Trigger.isAfter){
        handler.OnAfterInsert(Trigger.new);
    }
    //Before Update
    else if(Trigger.isUpdate && Trigger.isBefore){
        handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
    }
    //After Update
    else if(Trigger.isUpdate && Trigger.isAfter){
        handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap);
    }
    //Before Delete
    else if(Trigger.isDelete && Trigger.isBefore){
        handler.OnBeforeDelete(Trigger.old, Trigger.oldMap);
    }
    //After Delete
    else if(Trigger.isDelete && Trigger.isAfter){
        handler.OnAfterDelete(Trigger.old, Trigger.oldMap);
    }
    //After Undelete
    else if(Trigger.isUnDelete){
        handler.OnUndelete(Trigger.new);
    }
}

Trigger Handler Apex Class:

public with sharing class ObjectTriggerHandler {
    
    private boolean isExecuting = false;
    
    public ObjectTriggerHandler(boolean isExecuting){
        this.isExecuting = isExecuting;
    }
    
    public void OnBeforeInsert(List<Object> newObjects){
        //EXECUTE BEFORE INSERT LOGIC
    }
    
    public void OnAfterInsert(List<Object> newObjects){
        //EXECUTE AFTER INSERT LOGIC
    }
    
    public void OnBeforeUpdate(List<Object> oldObjects, List<Object> updatedObjects, Map<Id, Object> ObjectMap){
        //BEFORE UPDATE LOGIC
    }
    
    public void OnAfterUpdate(List<Object> oldObjects, List<Object> updatedObjects, Map<Id, Object> ObjectMap){
        //AFTER UPDATE LOGIC
    }
    
    public void OnBeforeDelete(List<Object> ObjectsToDelete, Map<Id, Object> ObjectMap){
        //BEFORE DELETE LOGIC
    }
    
    public void OnAfterDelete(List<Object> deletedObjects, Map<Id, Object> ObjectMap){
        //AFTER DELETE LOGIC
    }
    
    public void OnUndelete(List<Object> restoredObjects){
        //AFTER UNDELETE LOGIC
    }
    
    public boolean IsTriggerContext{
        get{ return isExecuting;}
    }
}