Calling the Domain layer

The Domain layer is positioned with respect to visibility and dependency below the Service layer. This in practice means that Domain classes should not be called directly from the execution context code, such as Visualforce Controllers, Lightning Component Controllers or Batch Apex, as it is the Service layer's responsibility to be the sole entry point for business process application logic.

That being said, we saw that the Domain layer also encapsulates an object's behavior as records are manipulated by binding Apex Trigger events to methods on the Domain class. As such, Apex Triggers technically form another point of invocation.

Finally, there is a third caller type for the Domain layer, and this is another Domain class. Restrict your Domain class callers to the following contexts only:

  • Apex Triggers: This calls via the fflib_SObjectDomain.handleTrigger method.
  • Service layer: This layer directly creates an instance of a Domain class via the new operator or through the Domain factory approach seen earlier in this chapter. Typically, the Domain class custom methods are called in this context.
  • Domain layer: Other Domain classes can call other Domain classes, for example, the Races Domain class can have some functionality it implements that also requires the use of a method on the Contestants Domain class.

Note

Note that it is entirely acceptable to have a Domain class with no Service class code referencing it at all, such as in the case where the Domain class solely implements the code for use in an Apex Trigger context and doesn't contain any custom Domain methods. Then, the only invocation of the Domain class will be indirect via the trigger context.

Service layer interactions

While implementing the compliance framework earlier in this chapter, we saw how a Domain class can be dynamically created within a generic service method. Typically, Domain classes are created directly. In this section, we will see a couple of examples of these.

The following code shows the Domain class constructor being passed records from a Selector class, which returns List<Contestant__c> for the purposes of this chapter. Notice that the Unit Of Work is created and passed to the Domain class custom method so that this method can also register work of its own to be later committed to the database:

public class ContestantService {
  public static void awardChampionshipPoints(
    Set<Id>contestantIds) {
     fflib_SObjectUnitOfWork uow = 
        Application.UnitOfWork.newInstance();

     // Apply championship points to given contestants 
     Contestants contestants = new Contestants(
           new ContestantsSelector().selectById(contestantIds));
     contestants.awardChampionshipPoints(uow);
     uow.commitWork();
  }
}

You might have noticed that this example reworks the Service example from the previous chapter by refactoring the code and calculating the points closer to the object for which the code directly applies by basically moving the original code to the Domain class.

This following second example also reworks the code from the previous chapter to leverage the Domain layer. In this case, the RaceService class also implements an awardChampionshipPoints method. This can also benefit from the Contestants Domain class method. Note how easy it is to reuse methods between the Service and Domain layers, as both ensure that their methods and interactions are bulkified.

public class RaceService {
  public void awardChampionshipPoints(Set<Id>raceIds) {
     fflib_SObjectUnitOfWork uow =
        Application.UnitOfWork.newInstance();
    List<Contestant__c> contestants = new List<Contestant__c>();
    for(Race__c race : new RacesSelector().selectByIdWithContestants(raceIds)) {
       contestants.addAll(race.Contestants__r);
    }
    // Delegate to Contestant Domain class
    new Contestants(contestants).awardChampionshipPoints(uow);
    // Commit work
    uow.commitWork();
  }
}

Domain layer interactions

In the following example (also included in the sample code for this chapter), a new Custom Object is created to represent the teams that participate in the races. The Driver object has gained a new Lookup field called Team to associate the drivers with their teams.

Leveraging the compliance framework built earlier, a Verify Compliance button for the Team records is also added to provide a means to check certain aspects of the team that are compliant (such as the maximum distance cars can cover during testing) as well as whether all the drivers in this team are also still compliant (reusing the existing code).

Note

This will be done by adding the compliance verification code in the new Teams Domain class call and by delegating to the Drivers Domain class method to implement the same for drivers within the team.

The following components have been added to the application to support this example:

  • A new Team object and tab
  • A new Testing Distance number field with a length of 6 on the Team object
  • A new Team lookup field on the Driver object
  • A new Teams Domain class

Tip

You don't always have to create an Apex Trigger that calls the fflib_SObjectDomain.triggerHandler method if you don't plan on overriding any of the Apex Trigger event handler methods.

Being able to call between the Domain layer classes permits the driver compliance-checking code to continue to be reused at the Driver level as well as from the Team level. This Domain class example shows the Teams Domain class calling the Drivers Domain class:

public class Teams extends fflib_SObjectDomain
  implements ComplianceService.ICompliant {
  public List<ComplianceService.VerifyResult> verifyCompliance() {
    // Verify Team compliance
    List<ComplianceService.VerifyResult>teamVerifyResults = 
      new List<ComplianceService.VerifyResult>();
    for(Team__c team : (List<Team__c>) Records) {
      ComplianceService.VerifyResulttestingDistance = 
        new ComplianceService.VerifyResult();
      testingDistance.ComplianceCode = '22.5';
      testingDistance.RecordId = team.Id;
      testingDistance.passed = team.TestingDistance__c!=null ?
        team.TestingDistance__c <= 15000 : true;
      testingDistance.failureReason = testingDistance.passed ?
        null : 'Testing exceeded 15,000km';
      teamVerifyResults.add(testingDistance);
    }

    // Verify associated Drivers compliance
    teamVerifyResults.addAll(
      new Drivers(
        new DriversSelector().selectDriversByTeam(
           new Map<Id, SObject(Records).keySet()))
.verifyCompliance());
    return teamVerifyResults;
  }
}

Note

The bulkification guideline applied to the Domain layer is being leveraged in the preceding code, as the Drivers Domain class logic was reused directly, with no changes, from the Teams Domain class. Also note that the implementation of the Drivers Domain class was and is still unaware of the split of drivers by team.

The following screenshot shows the new Team object and Compliance Checker component added to the Team page using Lighting App Builder. The team record and related driver record have compliance issues. For the team record an invalid Testing Distance value greater than 15,000 km. The FIA Super License field on the Driver record for Lewis Hamilton, which is unchecked, is not shown.

Domain layer interactions
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.177.86