I'm considering implementation of FFLib, or at least parts of it, throughout our enterprise. Right now I'm focusing on the Selector and Query classes to consolidate our SOQL into a Selector Layer. I'm having a hard time finding examples/discussions surrounding the Selector Class and how it would work (or wouldn't?) with SOQL for loops.
in the AccountsSelector of the fflib-apex-common-samplecode, AccountsSelector.selectById(idSet)
casts the return value of SObjectSelector.selectSObjectsById(idSet)
to a List<Account>
, and then returns the List to the caller.
public List<Account> selectById(Set<Id> idSet){
return (List<Account>) selectSObjectsById(idSet);
}
(For reference, the SObjectSelector.selectSObjectsById method looks like this):
public virtual List<SObject> selectSObjectsById(Set<Id> idSet){
return Database.query(buildQuerySObjectById());
}
So in the context of an AccountsService class, we might do:
List<Account> listAccountsById = AccountsSelector.newInstance().selectById(accountIds);
This works great as long as we aren't dealing with a lot of records or heap concerns. If we are, then we would leverage SOQL For Loops, as outlined in the Apex Developer Guide (emphasis mine):
SOQL for loops differ from standard SOQL statements because of the method they use to retrieve sObjects. While the standard queries discussed in SOQL and SOSL Queries can retrieve either the count of a query or a number of object records, SOQL for loops retrieve all sObjects, using efficient chunking with calls to the query and queryMore methods of the SOAP API. Developers should always use a SOQL for loop to process query results that return many records, to avoid the limit on heap size.
So, since the examples in the Selector layer of the fflib seem to focus on returning a List to the caller, I'm not clear on what the correct syntax / implementation is for using the Selector layer with SOQL For Loops. We of course want to maintain Separation of Concerns by consolidating all of the query logic into the Selector class, but then how do we get the benefit of leveraging the built-in performance benefits of SOQL for loops?
I've seen a few things online that say we should build the query separately using the QueryFactory and then use .toSOQL to return the string to Database.query in the for loop, something like...
fflib_QueryFactory query = new fflib_QueryFactory(Account.sObjectType);
query.selectField('name').selectField('Id').setCondition('Id In :accountIds');
for(Account objAccount : database.query(query.toSOQL()){
// Do Stuff
}
This of course breaks the separation of concerns principle by having query code inside of the Service layer. How do we properly use the fflib Selector layer design with SOQL for loops?
for (Account record : Database.query(MySelectors.buildSomeQuery()) { ... }