by Andrew Fawcett

A major theme in this series has been ensuring a good separation of concerns, making large complex enterprise level code bases more clear, self documenting, adaptable and robust to change, be that refactoring or functional evolution over time.

In the past articles in this series we have seen Service logic that orchestrates how functional sub-domains work together to form the key business processes your application provides. Domain logic breaks down the distinct behaviour of each custom object in your application by bring the power of Object Orientated Programming into your code.

This article introduces the Selector, a layer of code that encapsulates logic responsible for querying information from your custom objects and feeding it into your Domain and Service layer code as well as driving Batch Apex jobs.

Selectors.png

Contents

Introduction

A Selector layer contains code responsible for querying records from the database. While you can place SOQL queries directly inline within other layers, a few things may start to happen as the code complexity grows that you should consider.

  • Query inconsistencies, the same queries being made from different places for the same information and/or criteria (or at least subtle variants of the same) can lead to inconsistencies in your application. Perhaps certain criteria need to be applied and get lost as code is copy pasted around over time. If you think of a query as a piece of logic you'll soon start to realise it’s not a great idea to copy paste these around, just as it’s not such a good idea with code.
  • Query data inconsistencies, Once queried record data (essentially SObject's) start to flow around your logic, up and down, side to side around your code, calling methods receiving them start to become potentially fragile. A piece of code may have requested an Account or an Opportunity be passed to it, yet it cannot guarantee what fields have been queried and thus its content before hand. Developers may end up repeating another query over the same recordset simply to query fields it requires, or the code paths flow in such a way Account records queried by different queries get passed to a shared function.
  • Security inconsistencies, Salesforce requires that all Apex code adheres to the object security of the running user. Unfortunately, currently Apex runs at System Level, making it the developer’s responsibility to check this ahead of making the query. Fortunately it’s not a lot of Apex code to do this, but it can get overlooked easily and is not that easily tested for in unit tests.

Introducing the Selector Pattern

Mapper Pattern, "A layer of Mappers (473) that moves data between objects and a database while keeping them independent of each other and the mapper itself." Martin Fowler, EAA Patterns

You'll notice that this article is using the term Selector rather than Mapper. This better reflects a difference in the Force.com implementation of the Mapper pattern: one which my colleague Stephen Willock observed when developing our implementation of this pattern; one which the SObjectSelector base class introduced in this article is based on infact.

The difference here is that it is not really mapping a result set to a data class or domain representation, rather it is providing primarily SObject objects, the platform’s native way to express data queried from the database. In some cases, as you'll see later in this article, it can provide other representations as well, but essentially it is merely 'selecting' data in this instance, hence the name change!

A Selector should concern itself with providing the following.

  • Visibility, Reusability and Maintainability, making it easy to find and maintain database query logic. When updating query criteria and/or reflecting schema changes such as adding new commonly used fields with more ease and less risk to the rest of the code base. The selector should ideally always use the compile time references to field names even if dynamic queries are being constructed, helping ensure when fields are deleted the platform prevents this accordingly if references still exist in the code.
  • Predictability of queried data is important: it must be clear what it’s doing in the method name and what it’s returning. Returning partially populated records is not always a good model since it’s not clear to the caller or to whomever the caller passes the resulting data onto what was and was not populated field wise.
  • Security, implement for the caller any necessary security checks to honour the profile/permission set settings applied to the current user context.
  • Platform Sympathy, queries made should be as optimal as possible, mainly ensuring they operate on lists of whatever criteria are applicable, thus encouraging the caller to be bulkified in its code when calling of the selector methods. Selectors should also be able to balance the need for consistency of queried field data vs optimum field data where larger datasets are involved and heap maybe a concern.

Design Considerations

When writing a Selector class consider the following design aspects.

  • Naming, it is a good idea to group selector functionality by object type. If you follow the naming convention described in the Domain pattern article, you can group selector code with domain code, e.g. ProductsSelector or OpportunitiesSelector
  • Methods, methods can be static or none static, the latter does permit the use of a base class and common behaviour inheritance (as shown below). You may also want to consider caching results within a Selector instance. The names of the methods (typically prefixed by select) ideally should indicate the information and any related child information, e.g. selectById, selectByIdWithProductLines
  • Method Parameters, always take lists of any criteria the query uses. As per the rules for service and domain class logic, bulkification is key, so make this part of the contract of a selector class as well.
  • Returning SObject Lists, mainly selectors return SObject lists, while it is really easy to return Maps from SOQL queries the order of records will be lost this way (Apex does not supported ordered maps like Java). Either provide two method variants, selectByIdMap and selectByIdList or just have selectById and allow the caller to wrap the returned list in a Map if needed e.g. Map<Id, Account> accountsById = new Map<Id, Account>( accountsSelector.selectByIds(accoutIds) );
  • Returning QueryLocators, It might be tempting when implementing Batch Apex to implement your query inline in the start method, however this breaks the rules of encapsulation and reuse. Instead consider a selector method to return a QueryLocator e.g. accountSelector.selectByIdsWithQueryLocator(accountIds)
  • Returning Custom Recordsets, in some cases an Account or Opportunity may not be the best way to reflect the fields which have actually been returned by the query. If you only want to query a few fields (to avoid heap or view state issues for example) and/or you are selecting fields from various relationships or performing an aggregate query. Having the developer look into the Selector implementation to understand what information is and is not available is not a clear contact and thus fragile. Instead consider using a custom Apex class (Java calls them POJO's) to implement a wrapper for the query result to expose the query result fields more explicitly. There is an example of this later.

Implementing a Selector

The Selector implementation in this article uses a base class SObjectSelector to help build and in some cases execute the queries. It does this dynamically (while still ensuring compilation and reference integrity of the fields queried is maintained). It also provides some useful common query functionality.

  • Automatic inclusion of organisation dynamic fields, such as the CurrencyIsoCode field, that are only visible when the Multiple Currency feature is enabled.
  • Ability to include (optionally) fields defined by a Fieldset via the Administrator.
  • Enforces platform Security, by throwing an exception if the user does not have read access to the object, thus helping you pass the Salesforce Security Review!

The following shows the methods on the SObjectSelector base class, two abstract methods and two virtual methods. It provides a helper method selectSObjectsById to perform a query against the object and fields described by Id.

SelectorClass.png

Here is a basic example for the Product2 object, note that even though the selectSObjectsById method is available from the base class to be called directly, an overloaded version is implemented to clarify that its returning a list of Product2 records.

public with sharing class ProductsSelector extends fflib_SObjectSelector
{
	public List<Schema.SObjectField> getSObjectFieldList()
	{
		return new List<Schema.SObjectField> {
			Product2.Description,
			Product2.Id,
			Product2.IsActive,
			Product2.Name,
			Product2.ProductCode };
	}

	public Schema.SObjectType getSObjectType()
	{
		return Product2.sObjectType;
	}

	public List<Product2> selectById(Set<ID> idSet)
	{
		return (List<Product2>) selectSObjectsById(idSet);
	}
}

Implementing the getSObjectFieldList method defines a list of fields for the base class to query in the selectSObjectsById method or return from the getFieldListString method. This ensures the queries made using either of these methods result in records with fields which are always consistently populated. Helping resolve potential issues described above when inconsistently populated records can make for more fragile code and code paths.

The trade off here is potentially heap size vs how frequently fields are needed by callers of the selector methods. Use it to include only a balance of fields most of your logic will find most useful most of the time. Omit obscure or large text field fields in favour of providing these via more custom selector methods. Consider the option of custom selector methods as described further down this article.

The getOrderBy method can also be overridden, to ensure all queries built and/or executed by the base class share the same ordering criteria, as shown in the example below.

public override String getOrderBy()
{
	return 'SortOrder, PricebookEntry.Name';
}

The following code sample shows how a custom select method is written to return OpportunityLineItem that have recently been modified. The getFieldListString method returns a comma delimited list of the same fields expressed via the getSObjectFieldList method above. Note also that the assertIsAccessible method is called, this will throw an exception if the user does not have read access to the object.

public List<OpportunityLineItem> selectRecentlyUsed(Set<Id> accountIds, Integer recordLimit)
{
	assertIsAccessible();

	String query = String.format(
		'select {0} from {1} ' + 
		  'where Opportunity.Account.id in :accountIds ' +
		  'order by SystemModstamp DESC LIMIT :recordLimit', 
		new List<String>{ getFieldListString(), getSObjectName()});

	return (List<OpportunityLineItem>) Database.query(query);
}

This next example does not use the getFieldListString method and instead queries specific fields from the Opportunity, Account and User objects. The difference here is rather than returning an Opportunity with only those fields and relationships populated and expecting the caller to know how to reference the information it needs. It constructs around it a small Apex class to explicitly expose only the queried information. This is a much safer, self documenting and tighter contract to the caller. You may also want to consider this option instead of returning the rather generic AggregateDatabaseResult system class.

public List<OpportunityInfo> selectOpportunityInfos(Set<Id> idSet)
{
	List<String> selectFields = 
		new List<String> {  
			'Id', 
			'Amount', 
			'StageName', 
			'Account.Name', 
			'Account.AccountNumber', 
			'Account.Owner.Name' };
	String opportunityQuery = 
	String.format('select {0} from {1} where id in :idSet order by {2}', 
		new List<String> { 
		    String.join(selectFields, ','), getSObjectName() }, getOrderBy()))

	List<OpportunityInfo> opportunityInfos = new List<OpportunityInfo>();
	for(Opportunity opportunity : Database.query(opportunityQuery)
		opportunityInfos.add(new OpportunityInfo(opportunity));
	return opportunityInfos;	
}
	
public class OpportunityInfo
{		 
	private Opportunity opportunity;
	public Id Id { get { return opportunity.Id; } }		
	public Decimal Amount { get { return opportunity.Amount; } }		
	public String Stage { get { return opportunity.StageName; } }		
	public String AccountName { get { return opportunity.Account.Name; } }		
	public String AccountNumber { get { return opportunity.Account.AccountNumber;}}	
	public String AccountOwner { get { return opportunity.Account.Owner.Name; } }
	private OpportunityInfo(Opportunity opportunity) { this.opportunity = opportunity; }
}

FieldSet Support

Another feature of the SObjectSelector base class is to include fields referenced by a given FieldSet. This makes the Selector semi-dynamic and allows you to use the results in conjunction with Visualforce or Dynamic Apex code that requires the fields to have been queried. This example shows how this is done and also how a constructor parameter can be used to control the default inclusion of Fieldset fields and overridden by the caller.

public class ProductSelector extends fflib_SObjectSelector
{
	public ProductsSelector()
	{
		super(false);
	}
	
	public ProductsSelector(Boolean includeFieldSetFields)
	{
		super(includeFieldSetFields);
	}

	public List<Schema.SObjectField> getSObjectFieldList()
	{
		return new List<Schema.SObjectField> {
			Product2.Description,
			Product2.Id,
			Product2.IsActive,
			Product2.Name,
			Product2.ProductCode };
	}

	public Schema.SObjectType getSObjectType()
	{
		return Product2.sObjectType;
	}

	public List<Product2> selectById(Set<ID> idSet)
	{
		return (List<Product2>) selectSObjectsById(idSet);
	}
	
	public override List<Schema.FieldSet> getSObjectFieldSetList()
	{
		return new List<Schema.FieldSet> 
				{ SObjectType.Product2.FieldSets.MyFieldSet };
	}
}

Here is a short example of it in use (more examples of selectors in use are show below).

// Test data
Product2 product = new Product2();
product.Description = 'Something cool';
product.Name = 'CoolItem';
product.IsActive = true;
product.MyText__c = 'My Text Field';
insert product;
				
// Query (including FieldSet fields)
List<Product2> products = 
  new ProductsSelector(true).selectById(new Set<Id> { product.Id });
			
// Assert (FieldSet has been pre-configured to include MyText__c here)
System.assertEquals('Something cool', products[0].Description);		
System.assertEquals('CoolItem', products[0].Name);		
System.assertEquals(true, products[0].IsActive);		
System.assertEquals('My Text Field', products[0].MyText__c);

This capability is inspired by the work of Chris Peterson.

Reusing Field Lists between Selectors

As described above if you want to standardise on the fields you query you can leverage the selectSObjectsById and getFieldListString methods. The getFieldListString (and its variant getRelatedFieldListString) can actually be called from other Selector classes. In use cases where you are performing a sub query or accessing fields via lookup relationships. This ensures that no matter how the record is queried, either directly or indirectly the record field data is populated consistently with the fields you require.

Here is an example from the OpportuntiesSelector.selectByIdWithProducts method.

String query = String.format(
	'select {0}, ' +
	  '(select {3},{5},{6},{7} ' +
	     'from OpportunityLineItems ' + 
	     'order by {4}) ' + 
	  'from {1} ' +
	  'where id in :idSet ' + 
	  'order by {2}', 
	new List<String>{
         getFieldListString(),
         getSObjectName(),
         getOrderBy(),
         opportunityLineItemSelector.getFieldListString(),
         opportunityLineItemSelector.getOrderBy(),
         pricebookEntrySelector.getRelatedFieldListString('PricebookEntry'),
         productSelector.getRelatedFieldListString('PricebookEntry.Product2'),
         pricebookSelector.getRelatedFieldListString('PricebookEntry.Pricebook2')});

return (List<Opportunity>) Database.query(query);

Using a Selector

The following is a simple example, note that this Selector uses non-static methods to provide its functionality so an instance is created. This gives scope to perform some configuration and/or caching on the instance if needed.

List<Opportunity> opportunities = 
   new OpportunitiesSelector().selectById(opportunityIds));

The following example shows some Service layer code (you may recognise from the earlier article), which is now using a Selector to query and pass records to a Domain class which is performing some domain logic on the queried records.

Opportunities opportunities = new Opportunities(
    new OpportunitiesSelector().selectByIdWithProducts(opportunityIds));
opportunities.applyDiscount(discountPercentage, uow);

The following examples show a Selector being used in a Batch Apex start method to drive the records being processed in the execute.

public Database.QueryLocator start(Database.BatchableContext bc)
{
	return new InvoicesSelector().queryLocatorInvoicesToProcess();	
}

Testing Selectors

Unit testing Selector classes is much the same as any other class. However if you have included QueryLocator methods in your Selector, note that you can call the iterator method to obtain its resulting records and thus assert for the expected data in your Selector test.

// Query
Database.QueryLocator queryLocator = 
	new ProductsSelector().queryLocatorById(new Set<Id> { product.Id });	
Database.QueryLocatorIterator productsIterator = queryLocator.iterator();
Product2 queriedProduct = (Product2) productsIterator.next();

Summary

This article has presented a way to encapsulate another important kind of logic in your application, your query logic. Hopefully this article has given you some thoughts as to how to best look after and maintain this logic in a reliable way. That helps make the code using such queries more robust, easier to develop and maintain by referencing predefined queries exposed through your Selector layer. The base class illustrated in this article has also provided a point to provide greater consistency around your queries and support security best practices, as well as some nice additional features like Fieldset support.

This completes the current series of articles on Apex Enterprise Patterns. The main goal was to express a need for Separation of Concerns to help make your Apex logic on the platform live longer and maintain its stability. In presenting three of the main patterns I feel help deliver this. If nothing else, I'd like to think you have started to think more about SOC, even if it’s just class naming. Though hopefully beyond that if you use some of the articles’ various base classes great, please feel free to contribute to them. If you don't, or decide to skip some or use a different pattern implementation that's also fine, thats what patterns are all about. If you do, I'd love to hear about other implementations.

Martin Fowler's excellent patterns continue to be referenced on platforms, new and old. I've very much enjoyed adapting them to this platform and using them to effect better adoption of Force.com platform best practices along the way. Thanks to everyone for all the great feedback, long may your code live!

Links

Source Code

Those familiar with the code used in this series may notice a subtle change in the class naming. In effort to provide a pseudo namespace qualifier to the base and utility classes as well as reflect the evolution of the patterns beyond the initial Dreamforce 2012 session they were first introduced at. I’ve moved the classes into another GitHub repository here and applied a class naming prefix of ‘fflib_’ (note only a single underscore), with sample code here. Other than that the classes are much the same and in fact in the case of SObjectSelector carry a few new features, such as Fieldset support. The original Dreamforce 2012 repository is still present for reference.