This is the next in the series covering FFLIB and the enterprise design patterns. Previously I covered the unit of work pattern (UOW).

As mentioned in my introduction to FFLIB, the service layer is where we will place all of our business logic in our application. Think of the service layer of where you orchestrate all of your business logic.

It aims to solve one of the more common issues in larger applications, whereby logic becomes scattered or even worse, duplicated. By keeping our business logic defined in central place we encourage reuse and reduce logic from being scattered, increasing maintainability.

How would I use the service layer?

This layer, as mentioned, is for the containment of business logic within our application. Majority of our code will most likely reside in this layer.

Service layers can interact with all of the other patterns, such as:

Notice I mentioned service layer again in the list above. Services can call other services to become compound services.

What shouldn’t I do in the service layer?

Most logic can be performed within the service layer, but to really follow the enterprise design patterns we need to ensure we only place the correct logic within it to encourage testability and reusability.

One thing we shouldn’t be doing is performing any direct manipulation of sObjects here. When we start to manipulate sObjects in the service layer we’re not encouraging code reuse across the application as it’s placed in a layer which doesn’t easily expose the functionality.

Let’s take a look at the following example which updates the status of opportunities to “Closed won”.

public class MyService {

   public void updateOpportunities(List<Opportunity> records){
      for(Opportunity opportunity : records){
         opportunity.StageName = 'Closed won';
      }
      update records;
   }

}

This is manipulation of the sObjects and ideally shouldn’t be in the service layer. Try to move all of the above manipulation of the opportunities into the domain layer instead. This encourages reusability across the application.

Take a look at the following example which uses a UOW and domain layer to achieve the same functionality. First off, it is more code and in future posts I’ll be covering application structure which will help you write less code, but achieve the same thing.

public class MyService {

   public void updateOpportunities(List<Opportunity> records){
      fflib_ISObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(
         new List<Schema.SObjectType>{
            Opportunity.sObjectType
         }
      );
      Opportunities domain = new Opportunities(records);
      domain.markAsClosedWon(uow);
      uow.commitWork();
   }

}

The key to the above is that the service layer is delegating the updating of the stage name to the domain layer, whilst the unit of work is taking care of transaction control. In this case the method isn’t doing much by itself, it is only asking other layers to perform the actions.

What are good characteristics of a service layer?

A good service layer in our application should be caller agnostic, meaning it can be called from anywhere within the application without preconditions being met. Being caller agnostic allows our controllers, batch jobs and custom web services to confidently call the same piece of logic from anywhere in the application without any side effects.

What makes a service caller agnostic? The calling class or method does not need to set up preconditions in order to call the service layer, i.e. all of the logic is self contained within the service.

It’s also a good idea to offer overloaded versions of your methods inside the service layer for accepting a unit of work (UOW) instance when your service method uses UOW itself. This allows the calling method to pass in their own UOW instance and pass it around the application in confidence that it avoids hitting limits and ensuring maximum performance. Have a look at the following example:

public class MyService {

   public void updateOpportunities(List<Opportunity> records){
      fflib_ISObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(
         new List<Schema.SObjectType>{
            Opportunity.sObjectType
         }
      );
      updateOpportunities(records);
   }

   public void updateOpportunities(List<Opportunity> records, fflib_ISObjectUnitOfWork uow){
      Opportunities domain = new Opportunities(records);
      domain.markAsClosedWon(uow);
      uow.commitWork();
   }

}

You can see in the example above we overloaded the methods to include one which accepts a unit of work instance and the other instantiating one for us.

So that’s the service layer

The service layer is actually quite simple to set up and use. What is challenging is understanding what should be placed in this layer and what shouldn’t. As with most things with programming, practice makes perfect.

In my next blog post I will be covering application structure with FFLIB on how to bring all of the different layers together to get an awesome foundation to your Salesforce application.