SoC and the Apex Common Library Tutorial Series Part 13: The Selector Layer

What is the Selector Layer

The Selector Layer is Salesforce is based off 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 code base 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.


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 I 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 examples 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(acctIds);
    } 

    private without sharing Account_Selector_WithoutSharing{
        public List<Account> selectAccountsByIdElevated(Set<Id> acctIds){
            return [SELECT Id FROM Account WHERE Id IN : acctIds]; 
        }
    }
}

Implementing the Selector Layer

To find out how to implement the Selector Layer using Apex Commons, 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, the only other option I’m aware of at this time is Query.apex. Outside of that, you could certainly roll your own selector layer, but it is no small feat if done right.

Libraries That Could Be Used for the Domain 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


Next Section

Part 14: Implementing the Selector Layer with the Apex Common Library

SoC and the Apex Common Library Tutorial Series Part 14: Implementing the Selector Layer with the Apex Common Library

The Template for every Selector Class you create

Every Selector layer class you create should at least implement the following methods for it to work as anticipated.

//Selector layer classes should all use the inherited sharing keyword so that the caller //determines to context it operates in.
//It should also always extend the fflib_SObjectSelector so it can inherit that classes //methods and functionality.
public inherited sharing class Contact_Selector extends fflib_SObjectSelector
{
	public Contact_Selector(){
                /*This is calling the fflib_SObjectSelector classes constructor and setting 
                  the following booleans:
                  1) If the selector queries should use field sets
                  2) If you would like to enforce CRUD security
                  3) If you would like to enforce FLS
                  4) If you would like to sort selected fields
                */
		super(false, true, true, false);
	}

        //Add the base fields for the object that should just be used in absolutely every 
        //query done by this class
	public List<Schema.SObjectField> getSObjectFieldList(){
		return new List<Schema.SObjectField>{
				Contact.Id,
				Contact.Name,
				Contact.FirstName,
				Contact.LastName
		};
	}

        //Allows you to easily get the object type for the object being queried by this class
	public Schema.SObjectType getSObjectType(){
		return Contact.SObjectType;
	}

        //Allows you to create a query that selects records by a set of ids (basically adds 
        //the WHERE Id IN :ContactIds to the query)
	public List<Contact> selectById(Set<Id> contactIds){
		return (List<Contact>) selectSObjectsById(contactIds);
	}
}

The fflib_SObjectSelector Constructor Parameters and what each of them mean

When you create a Selector class that extends the fflib_SObjectSelector class you have the option to send some parameters to its constructor that determine how to the selector class functions. They are the following (in order of parameters passed to the constructor):

1) Would you like to allow your class to use field sets when building the initial query fields for your selector layer class? By default this parameter is set to false.

2) Would you like to enforce CRUD (Object level create, read, update, delete) security on your selector layer class? By default this parameter is set to true.

3) Would you like to enforce FLS (Field level security) on your selector layer class? By default this parameter is set to false.

4) Would you like your selected fields in your query to be sorted alphabetically when your query is created (this is literally just a sort call on a list of strings in the code)? By default this parameter is set to true.

If you would like to alter any of these settings for your selector class you can do the following:

public inherited sharing class Contact_Selector extends fflib_SObjectSelector
{
        //In our selector classes constructor we are calling the fflib_SObjectSelector using the super() call
        //and setting out parameters.
	public Contact_Selector(){
                //This is completely optional. If you like the defaults listed above you do not have to call the super class constructor at all.  
		super(false, true, true, false);
	}
}

Note that this is completely optional, if you like the default behavior listed above, just don’t bother calling the super method and overriding them!


How to set the default field selection for your Selector Class

One of the biggest benefits of the selector layer is selected field consistency in your queries. If 99% of the time you are querying for the subject field on your Case object, why not just query for it by default without ever thinking about it again right?? Well the getSObjectFieldList method that you implement in your Selector classes does just that. Here’s how to implement that method in your selector:

public List<Schema.SObjectField> getSObjectFieldList(){
        //Note that this is using concrete references to the fields, not dynamic soql (here at 
        //least). This is to ensure the system knows
        //your code is dependent on these fields so you don't accidentally delete them some 
        //day.
	return new List<Schema.SObjectField>{
                        //In this list, place every field you would like to be queried for by 
                        //default when creating a query
                        //with your selector class.
			Contact.Id,
			Contact.Name,
			Contact.FirstName,
			Contact.LastName
	};
}

If you choose to enable field sets to allow for default field selection for your selector class, you can implement the following method to select fields from the field set to be included in your queries:

public override List<Schema.FieldSet> getSObjectFieldSetList(){
    return new List<Schema.FieldSet>{SObjectType.Case.FieldSets.CaseFieldSetForSelector};
}

The major benefit of using the field sets for query fields is you can add new fields on the fly without adding extra code. This becomes super important if you’re building a managed package.


How to set your default OrderBy clause in your Selector Class

By default all Selector Layer Classes that extend the fflib_SObjectSelctor are ordered by the Name field (if the name field isn’t available on the queried object it defaults to CreatedDate). If that’s kewl with you, no need to override it, but if you’d prefer the default sort order for your select class be different then you just need to override the following method like so:

public override String getOrderBy(){
    return 'Subject';
}

If you wanted to order by multiple fields you would just do the following (basically just create a comma separated list in the form of a string):

public override String getOrderBy(){
    return 'Subject, Name, CustomField__c, CustomLookup__r.Name';
}

The fflib_QueryFactory class and Custom Queries

Chances are you’re gonna wanna query something that’s not immediately available via the fflib_SObjectSelect class, well lucky for you the fflib_QueryFactory class is here just for that! Here is an example of a custom query via the use of the fflib_QueryFactory class (a list of all available query factory methods are at the bottom of this wiki article).

//This allows us to select all new cases in the system using the QueryFactory in //fflib_SObjectSelector
public List<Case> selectNewCases(){
    return (List<Case>) Database.query(newQueryFactory().
    selectField(Case.Origin).
    setCondition('Status = \'New\'').
    setLimit(1000).
    toSOQL());
}

There are also tons of other methods that allow you to add offsets, subselects, set ordering and more (outlined in the method cheat sheet section).


How to Do Sub-Select Queries (Inner Queries)

There’s a kinda handy way to do sub-select queries by using the fflib_QueryFactory class. You can use it in your selector class’s methods as outlined below:

//Pretend this method exists in a Case_Selector class
public List<Case> innerQueryExample(){
    //Using this Case_Selectors newQueryFactory method that it inherits from the 
    //fflib_SObjectSelector class it extends
    fflib_QueryFactory caseQueryFactory = newQueryFactory();

    //Creating a new instance of our Task_Selector class and using the 
    //addQueryFactorySubselect method to add the task query as a 
    //inner query for the caseQueryFacorty
    fflib_QueryFactory taskSubSelectQuery = new 
    Task_Selector().addQueryFactorySubselect(caseQueryFactory);
  
    //Querying for our data and returning it.
    return (List<Case>) Database.query(caseQueryFactory.toSOQL());
}

How to do Aggregate Queries

There’s not really a built-in method in either the fflib_SObjectSelector or the QueryFactory class to deal with this. So you just deal with aggregate queries by building a method in your Selector Class in the following way:

public List<AggregateResult> selectAverageTacosPerTacoBell(){
    List<AggregateResult> tacoData = new List<AggregateResult>();
    for(AggregateResult result: [SELECT Id, AVG(Tacos_Sold__c) avgTacos FROM Taco_Bell__c 
        GROUP BY Taco_Type__c]){
        tacoData.add(result);
    }
    return tacoData;
}

The fflib_SObjectSelector Class methods Cheat Sheet

While there are many other methods within the fflib_SObjectSelector class below are the methods most commonly utilized in implementations.

1) getSObjectName()_ – Returns the name of the object that your select was built for.

2) selectSObjectsById(Set<Id> idSet) – Returns a query that selects all the records in your id set. It constructs a query based on the fields you declare in your selector class’s getSObjectFieldsList method and orders them by whatever is represented in the m_orderBy variable.

3) queryLocatorById(Set<Id> idSet) – This method is basically the same as the selectSObjectsById method except that it returns a query locator instead. This should be leveraged for batch classes that need query locators.

4) newQueryFactory – This method returns a new instance of the fflib_QueryFactory class and uses your selector classes fields listed in the getSObjectFieldsList method and orders them by whatever you set the default orderby to in your selector class.

5) addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory) – This method basically just creates a subselect query (an inner query) for the “parentQueryFactory” and then returns the parent query with a subselect query that represents the query for your objects selector class (more info on how this method works in the sub-select query section in this wiki article).


The fflib_QueryFactory Class methods Cheat Sheet

While there are many other methods within the fflib_QueryFactory class below are the methods most commonly utilized in implementations.

1) assertIsAccessible() – This method checks to see if the running user can actually read to object this selector was built for.

2) setEnforceFLS(Boolean enforce) – This method determines whether or not to enforce field level security on the query you are running.

3) setSortSelectFields(Boolean doSort) – If you would like the list of fields selected to be selected in alphabetical order, you can use this method to make your selected fields be selected in alphabetical order.

4) selectField(Schema.SObjectField field) – Use this method to select a field for your query.

5) selectFields(Set fields) – Use this method to select multiple fields for your query at the same time.

6) selectFieldSet(Schema.FieldSet fieldSet) – Use this method to select fields for your query from a field set.

7) setCondition(String conditionExpression) – Use this method to set the conditions for your query (basically you are setting the WHERE clause in your query. Do NOT add the WHERE to the string you pass into to this method).

8) setLimit(Integer limitCount) – Sets a limit for your query.

9) setOffset(Integer offsetCount) – Sets an offset for your query.

10) addOrdering(Ordering o) – Sets up an ORDER BY clause for your query. You need to build an fflib_QueryFactory.Ordering object to pass to this method. There are a ton of ordering method permutations in this class, so definitely check them all out.

11) subselectQuery(String relationshipName) – Sets up a subquery for the query. You need to pass in a string representation of the relationship fields developer name to this method.

12) toSOQL() – Takes the fflib_QueryFactory you’ve just built and turns it into an actual string representation of the query that you can use in a Database.query() call.

13) deepClone() – Creates a clone of your fflib_QueryBuilder query in case you’d like an easy way to generate another permutation of this query without rebuilding the whole thing.


Next Section

Part 15: The Difference Between Unit Tests and Integration Tests