We’ve covered all of the layers so far in use when using FFLIB. Here’s a quick overview of the different layers again.

  • Selector – all of your queries go into this layer.
  • Domain – the layer which contains reusable pieces of code for working with sObjects and is the entry point for handling trigger events.
  • Service – business logic orchestration layer which can be called from anywhere in your application.
  • Unit of work – a pattern for managing transactions on the platform.

In this blog post I’m going to cover how to “stitch” all of those layers together to produce a rock solid code base and have an application structure which is properly set up.

The application class

In my previous blog posts I demonstrated how to use the different layers on their own, but not how to set up your application from the get-go to enable dependency injection. Why is this important?

  • Ready to take advantage of ApexMocks for improved unit testing.
  • Ability to switch out business logic for newer versions with minimal effort.

An application class is responsible for defining which classes are responsible for handling the different types of business logic requests within our code base. For example, for the Account sObject we can define which selector to use or for the Contact sObject which domain class to use.

When you install FFLIB it doesn’t come with an Application class ready for you to start editing, so we have to create this ourselves. This is a one-time activity per project and is a quick process to setup. You can either copy the below code or use the Application from the sample repository.

Getting started

Below is a sample application class which will be your central point for retrieving the implementations for the unit of work, services, selectors and domains.

public class Application {

   public static final fflib_Application.UnitOfWorkFactory unitOfWork =
      new fflib_Application.UnitOfWorkFactory(
         new List<SObjectType> {
         }
      ); 

   public static final fflib_Application.ServiceFactory service =
      new fflib_Application.ServiceFactory(
         new Map<Type, Type> {
         }
      );

   public static final fflib_Application.SelectorFactory selector =
      new fflib_Application.SelectorFactory(
         new Map<SObjectType, Type> {
         }
      );

   public static final fflib_Application.DomainFactory domain =
      new fflib_Application.DomainFactory(
         Application.Selector,
         new Map<SObjectType, Type> {
         }
      );

}

You might have noticed that all of the properties within this class point towards a factory within the fflib_Application class. This is because this class is a factory, we ask it to give us an implementation based on a given request, but we don’t care how it’s retrieved or what it is.

Now we have the basics set up, let’s take a look at how we configure this class. Later, we will cover how to use this class.

Unit of work

In an earlier blog post we covered how the unit of work operates, we must define the order in which our sObjects are written to the database. So, that is what we must do within here as well.

When inserting accounts and contacts which have a relationship defined, then we know we must insert the account first and then the contacts. We define this order of precedence as follows within the unitOfWork property:

public static final fflib_Application.UnitOfWorkFactory unitOfWork =
   new fflib_Application.UnitOfWorkFactory(
      new List<SObjectType> {
         Account.sObjectType,
         Contact.sObjectType
      }
   ); 

Services

The business orchestration logic is contained within the services layer. Each service ideally within our application should also implement an interface to enable easier switching out for newer versions or different behaviours at runtime.

With this in mind it’s easier to understand the following piece of code. We must tell the FFLIB ServiceFactory that when the application requests the implementation for a given interface then it knows which implementation to return. I.e. the application asks for the business logic for ICustomerService interface and is given an instance of CustomerService.

public static final fflib_Application.ServiceFactory service =
   new fflib_Application.ServiceFactory(
      new Map<Type, Type> {
         ICustomerService.class   =>   CustomerService.class
      }
   );

Selectors

Similar to how services are defined within the application class, for selectors we must define which selector should be used for each different type of sObject we have selectors for.

public static final fflib_Application.SelectorFactory selector =
   new fflib_Application.SelectorFactory(
      new Map<SObjectType, Type> {
         Account.sObjectType   =>   AccountsSelector.class,
         Contact.sObjectType   =>   ContactsSelector.class
      }
   );

Domains

The domain layer we need to do a little more compared with the other layers we’ve covered so far.

First, there is an additional argument we pass into the constructor of the DomainFactory which is a reference to SelectorFactory which should be used by the domain layer for loading records. This should typically be the same SelectorFactory instance being used within the Application class.

public static final fflib_Application.DomainFactory domain =
   new fflib_Application.DomainFactory(
      Application.Selector,
      new Map<SObjectType, Type> {
      }
   );

The second part which we need to cover is how we register our domain layers. Like the selectors definitions, we need to define for which domain class within our application is responsible for a given sObject.

public static final fflib_Application.DomainFactory domain =
   new fflib_Application.DomainFactory(
      Application.Selector,
      new Map<SObjectType, Type> {
         Account.sObjectType   =>   Accounts.Constructor.class
      }
   );

Due to limitations within Apex, we cannot pass into the constructor of the domain layer a list of accounts directly, so we have to pass them into the constructor via a different approach. This is done via an inner class within the domain layer which implements fflib_SObjectDomain.IConstructable. Our inner class must then implement a method called “construct” and accepts a list of sObjects. Our inner class can now instantiate an instance of our domain layer (the outer class) and pass along the generic list of sObjects to a list of concrete sObject types.

public with sharing class Accounts extends fflib_SObjectDomain implements IAccounts {

   public Accounts(List<Account> sObjectList){
      super(sObjectList);
   }

   public class Constructor implements fflib_SObjectDomain.IConstructable {
      public fflib_SObjectDomain construct(List<SObject> records){
         return new Accounts(records);
      }
   }

}

The inner class approach isn’t nice and it’s additional boilerplate code which needs to be added, but it’s a clean solution given the limitations within Apex. It’s a cut and paste exercise when setting up new domain classes within your application.

Coming back to how we define which class is responsible for each sObject type we have a domain class for, we need to point to our inner class instead and not the outer domain class.

Account.sObjectType   =>   Accounts.Constructor.class

Using the application class

We’re getting closer to being able to take advantage of our new application class!

Unit of work

The unit of work layer is already available without doing anything extra besides defining the order of precedence of the sObjects. Below is how we ask the application to give us a new unit of work instance to work with.

fflib_ISObjectUnitOfWork uow = Application.unitOfWork.newInstance();
uow.registerNew(
   new Account(Name = 'Hello world!')
);
uow.commitWork();

Notice that we didn’t instantiate a class directly, we asked for an instance to be given to us instead. Additionally, we’re coding towards an interface and not a concrete class!

Services

Unlike how we get a unit of work instance, for services we need to ask for an implementation of our service which has been registered for a given interface type.

Below is an example how to ask for an instance of the real service registered for the ICustomerService interface. Again, we never instantiate the CustomerService class directly.

ICustomerService service = (ICustomerService) 
         Application.service.newInstance(ICustomerService.class);

Our application is again coded towards an interface and never a real concrete implementation!

This is a lot of typing to get an instance of our service even before we can start calling methods. As a recommendation, add a static method to your service class (CustomerService) which creates a shortcut:

public static ICustomerService newInstance(){
   return (ICustomerService) Application.service.newInstance(ICustomerService.class);
}

You can then create an instance of the service by calling the static method instead which is much more compact and easier to read.

ICustomerService service = CustomerService.newInstance();

Selectors

You should’ve started to notice a pattern, we ask the application for the implementation for a given type. Selectors are no different. We ask for an instance of a selector based on the sObjectType you need to run a query for.

IAccountsSelector selector = (IAccountsSelector) 
         Application.selector.newInstance(Account.SObjectType);

In the above example we get an instance of a selector for the Account sObject type. The real class being instantiated is “AccountsSelector”.

Typing out all of the above code to run a query is going to get painful quite quickly, instead add the small piece of code to your selectors as a shortcut instead:

public static IAccountsSelector newInstance(){
   return (IAccountsSelector) Application.selector.newInstance(Account.SObjectType);
}

You can then use your selector by using the following more compact code instead which is easier to remember and easier to type:

List<Account> accounts = AccountsSelector.newInstance().selectById(accountIds);

Domains

Using domains using the application class is super easy and extremely useful from a developer perspective. As the domain layer is responsible for working with sObjects it is useful to have the domain layer instantiated with the list of records already set within it.

Below is an example how to instantiate the domain layer for a list of account records.

List<Account> accountRecords = new List<Account>{
   new Account(Name = 'Hello world!')
};
IAccounts domain = (IAccounts) Application.domain.newInstance(accountRecords);

The key part is the following piece of code:

Application.domain.newInstance(accountRecords)

What is happening here is that FFLIB determines which type of sObjectType the list is a type of, in this case Account and then it determines which class is the real domain class (in our case this is Accounts.Constructor). The real class is then instantiated and the “construct” method is called and our list of account records is passed into it, which in turn the outer Accounts class is instantiated and the records are passed into its constructor.

Just like the earlier examples, using the domain layer like this in your application is going to get painful quite quickly as it’s a lot to type out every time you want to use a domain. Add a static method to your domain layer class to create a shortcut:

public static IAccounts newInstance(List<Account> sObjectList){
   return (IAccounts) Application.domain.newInstance(sObjectList);
}

You can then use the domain in the following more compact approach:

List<Account> accountRecords = new List<Account>{
   new Account(Name = 'Hello world!')
};
IAccounts domain = Accounts.newInstance(accountRecords);

Wrapping up

Setting up your application initially to use the above structure can take some time, but it really pays off when you start to scale your application. Your developers will know to find things much more quicker than before and as you will see in future posts it will become extremely easier to unit test!

Definitely take a look at the sample repository for more examples on how to set your application to use FFLIB:

https://github.com/financialforcedev/fflib-apex-common-samplecode/blob/master/fflib-sample-code/

Happy coding!