5

I am trying to follow a single trigger approach.

  • there are several resources and patterns for it. I am doing something very simple

My main question: Whenever we need to add new functionality to the trigger (aka a new service class), how do I reference it? which data records am I passing to it? I don't want, nor think, that all logic needs to go in a single handler and/or class. So, if I seperate the logic to several 'Service classes' what's the approach to reference the data set? Can I always use either:

  • trigger.new
  • trigger.newmap

are they static to the trigger?

Do I need to return form a service class the list of 'trigger.new' that it received and may be modified?

Trigger:

trigger OpportunityTrigger on IJS__c (
before insert, after insert,
before update, after update, 
before delete, after delete)
{

    OpportunityHandler handler = new OpportunityHandler();

    if (Trigger.isBefore)
    {
        if (Trigger.isInsert)
        {
            handler.OpportunityService1(trigger.new);

            handler.OpportunityService2(trigger.new);
        }
        if (Trigger.isUpdate) { }
        if (Trigger.isDelete) { }
    }
    if (Trigger.IsAfter){
        if (Trigger.isInsert){ } 
        if (Trigger.isUpdate){ }
        if (Trigger.isDelete){  } 
    }
}

A Single Trigger Handler:

public without sharing class OpportunityHandler
{
    public void OpportunityService1 (List<Opportunity> objList)
    {
        // call Servicefunction1 and modify the data 

    }
    public void OpportunityService2 (List<Opportunity> objList)
    {
        // call ServiceFunction2 and modify the data 
    }
}

Several Service Classes

public without sharing class OpportunityService1 {

public OpportunityService1() {}

public void ServiceFunction1(){
    for(Opportunity opp :updatedOpportunitiesList)
    {
       opp.recordtypeid = system.label.SomeRecordTypeId;
    } 
}}

Second Service Class:

public without sharing class OpportunityService2 {

public OpportunityService2() {}

public void ServiceFunction1(){
    for(Opportunity opp :updatedOpportunitiesList)
    {
       opp.Name = 'set a new name';
    } 
}}
}
1
  • 1
    I'm partial to the fflib (Force.com Enterprise Architecture) patterns illustrated on Trailhead
    – cropredy
    Commented Aug 28, 2018 at 16:23

1 Answer 1

3

The usual pattern that I follow is a "factory" pattern with inheritance in action. In that case I would keep a parent class, which accepts the trigger.new or trigger.newMap values and then any service class will extend the parent class and thus has the values available during the lifecycle.

So think of something as below. This is just a sample, but I have used different versions of this different times. E.g., I would also pass the tigger.new or tigger.newMap as parameters to the base class sometimes. But overall, keeping a base class just simplifies the movement of the variables and follows a typical Object Oriented pattern here allowing inheritance and code re-usability.

public virtual class BaseClass {
    private List<SObject> newObjs; // expose using get/set methods
    private Map<Id, SObject> newIdMap; // expose using get/set methods
    ....
    public BaseClass() {
        newObjs = Trigger.new;
        newIdMap = Trigger.newMap;
    }
    ... all my other stuff ...
 }

Then you have child services extending this class as:

public class Service1 extends BaseClass {
    public Service1() {
       super();
    }
    ... these classes will always have the reference of the new/newMap ...
 }

And then you initialize the BaseClass from your trigger.

trigger AccountTrigger on Account (before insert ...) {
    Service1 s1 = new Service1();
    Service2 s2 = new Service2();
    ... all other stuff ...
}

As for your questions:

are they static to the trigger?

The answer is no, as you can see above. Trigger context variables can be used in Apex classes within the transaction context. It's just that you should be aware of what are you expecting in those list/maps.

Whenever we need to add new functionality to the trigger (aka a new service class), how do I reference it? which data records am I passing to it? I don't want, nor think, that all logic needs to go in a single handler and/or class. So, if I seperate the logic to several 'Service classes' what's the approach to reference the data set?

The above approach lets you to just create a new service class and extend the base class and refer the variables as soon as your class is initialized, as you are assigning the variables within the constructors.

Do I need to return form a service class the list of 'trigger.new' that it received and may be modified?

No, because its being referenced in the service classes within the same transaction context.

Hope this helps.

3
  • Thank you. This is a good approach. Still: Question: Can I use Trigger.new in one service class, and Trigger.newMap in the other?
    – Saariko
    Commented Aug 28, 2018 at 14:51
  • 1
    Yes, you can use the trigger context variables in your classes as required. A good way is usually to pass it to the base class, either as a list/map either during initialization of the base class itself or pass it from child class. Whichever approach you take, but it should be accessible in your service class as you need it.
    – Jayant Das
    Commented Aug 28, 2018 at 15:19
  • 1
    I would also consider passing for update Trigger.old and Trigger.oldMap - Just know that delete instances you will not be able to work in the after with Trigger.new (judging by your trigger instances ) - it is a case you will want to ensure not to run into null references to Trigger.new / Trigger.newMap.
    – Ronnie
    Commented Aug 29, 2018 at 0:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .