Skip to main content
01 Separation of ConcernsChapter 13 of 2020 Force DI
Chapter 13 · The Selector layer

The Selector Layer

Your org's SOQL queries live here.

7 min readVideo 27:31Standard template

What is the Selector Layer?

The Selector Layer in Salesforce is based on Martin Fowler's Data Mapper Layer concept. It's "a layer of Mappers that moves data between objects and a database while keeping them independent of each other and the Mapper itself".

In most tech stacks when you want to represent records in a database table you create classes in your codebase to represent them to hold them in memory and prep them for transit to the actual database. These are what Martin references as "objects" in the above quote. Salesforce already creates these classes for you in the background to represent your Custom and Standard Objects. It's why you can just inherently write Case cs = new Case(); in Apex and it creates a brand new Case record for you.

Since Salesforce already does that work for you, the Data Mapper Layer, just turns into the Selector Layer and within the Selector Layer we are basically just concerned with housing and performing queries for our respective objects. You will ideally call upon the Selector Layer every single time you need to make a query in Apex. The largest goal with this layer is to avoid having repetitive queries everywhere within the system and to have some overall consistency with your object queries (the fields always queried, limits, order by clause, etc).


When to make a new Selector Layer Class

Whenever you need to create queries on an object you've never done queries on before, you would create a new Selector Layer Class. So for instance if you needed to create some queries for the Case object to use in your service or domain layer classes, you would create a Case Selector class. There should typically be one selector class per object you intend to query on, however in larger orgs you might want to create an object selector per application as well. For instance you might have Opportunities_App_A_Selector and Opportunities_App_B_Selector. When your org grows and your selectors get large, breaking them out by application type is very helpful to make them easier to maintain.

A selector that extends nothing is still worth having

If you're migrating an existing org, don't let the fflib_SObjectSelector base class (covered in chapter 14) become the barrier to starting. Simply creating classes with the Selector suffix and re-homing your inline SOQL into them buys you reuse and encapsulation immediately - the consistency benefits of the base class can come later, and extending it for some selectors while others remain plain classes is perfectly fine during the transition.


Selector Layer Naming Conventions

Class Names - Your classes should ideally follow the naming conventions of the domain layer just with Selector appended to the end of them, unless you have common cross-object queries then it's a bit different.

Selector Class Naming Examples (Note that that the writer prefer underscores in names, this is personal preference):

Accounts_Selector
Opportunities_Selector
OpportunityQuotes_Selector

Method Naming and Signatures - The names of the methods in a selector class should all start with the word "select". They should also only return a list, map or QuerySelector and should only accept bulkified parameters (Sets, Lists, Maps, etc). A few good examples of method signatures are below.

Selector Method Examples

public List<sObject> selectById(Set<Id> sObjectIds)
public List<sObject> selectByAccountId(Set<Id> accountIds)
public Database.QueryLocator selectByLastModifiedDate(Date dateToFilterOn)

Selector Layer Security

The Selector Layer classes should all ideally inherit their sharing from the classes calling them. So they should typically be declared as follows:

public inherited sharing class Account_Selector

If there are queries for your object that you absolutely must have run in system context (without sharing) you would want to elevate those permissions through the use of a private inner class like so:

public inherited sharing class Account_Selector
{
public List<Account> selectAccountsById(Set<Id> acctIds){
return new Account_Selector_WithoutSharing().selectAccountsByIdElevated(this, acctIds);
}

private without sharing class Account_Selector_WithoutSharing{
public List<Account> selectAccountsByIdElevated(Account_Selector outerSelector, Set<Id> acctIds){
//The outer selector rides along so this elevated query can reuse its
//field lists / query building instead of hand-writing a divergent query
return [SELECT Id FROM Account WHERE Id IN : acctIds];
}
}
}

Notice the inner class receives the outer selector as a parameter. In this bare-bones example it's just a convention, but once your selectors extend fflib_SObjectSelector it becomes genuinely important: the inner class can call outerSelector.newQueryFactory() and inherit the selector's standard field list, ordering and configuration - so your elevated query stays consistent with every other query the selector runs, instead of becoming a hand-written one-off that silently drifts as fields get added. More on newQueryFactory in chapter 14.


Implementing the Selector Layer

To find out how to implement the Selector Layer using Apex Common, continue reading here: Implementing the Selector Layer with the Apex Common Library. If you're not interested in utilizing the Apex Common Library for this layer there are pretty sparing options out there that are pre-built, but there are others such as Query.apex. You could certainly roll your own selector layer, but it is no small feat if done right.

Libraries That Could Be Used for the Selector Layer

Apex Common (Contains a framework for all layers)

Query.apex


Selector Layer Examples

Apex Common Examples (Suggested)

Case Object Selector Example (Lots of comments)

Contact Object Selector Example

Task Object Selector Example

Non-Apex Common Examples

Case Object Selector Simple Example