3

We are implementing Apex Enterprise Design patterns in our org and I'm struggling to understand the difference between fflib_SObjectDomain and fflib_SObjects. There's very little documentation on the two. I've gone through Matt Gerry's excellent video tutorial "The Complete Guide To The Apex Common Library" and he makes no distinction between these classes and references fflib_SObjectDomain but not fflib_SObjects in his video. I've also completed the related Trailmix related to the Apex Common Library and again, it doesn't mention the fflib_SObjects class at all.

I've studied the sample application implementing the Apex Common Library and this further adds to the confusion as the Application class example maps classes of type fflib_SObjects whilst while defining the DomainFactory instead of mapping domain classes of type fflib_SObjectDomain.

public static final fflib_Application.DomainFactory Domain = 
    new fflib_Application.DomainFactory(
        Application.Selector,
        new Map<SObjectType, Type> {
                Opportunity.SObjectType => Opportunities.Constructor.class,
                OpportunityLineItem.SObjectType => OpportunityLineItems.Constructor.class,
                Account.SObjectType => Accounts.Constructor.class,
                DeveloperWorkItem__c.SObjectType => DeveloperWorkItems.class });

Further adding to the confusion, the sample application calls fflib_SObjectDomain classes TriggerHandlers instead of Domain classes.

Finally, in opposition to the sample application, Matt Gerry's (Coding With The Force) example code maps fflib_SObjectDomain classes in his Application class, instead of fflib_SObjects whilst defining the DomainFactory (see Cases for example):

//This allows you to create a factory for instantiating domain classes. You can send it a set of record ids and
//you'll get the corresponding domain layer.
//Example initialization: fflib_ISObjectDomain objectDomain = Application.domain.newInstance(recordIds);
public static final fflib_Application.DomainFactory domain =
        new fflib_Application.DomainFactory(
                Application.selector,
                new Map<SObjectType, Type>{Case.SObjectType => Cases.Constructor.class,
                Contact.SObjectType => Contacts.Constructor.class,
                Account.SObjectType => Accounts.Constructor.class}
        );

So to pull this back around to my original question, what's the difference between fflib_SObjectDomain and fflib_SObjects and should I be mapping these fflib_SObjects clases in my DomainFactory or should I be mapping fflib_SObjectDomain classes in my DomainFactory?

Thank you.

4
  • My best guess is fflib_SObjects is a collections class wrapper of sorts but what makes me doubt this is that the sample application appears to map them as Domain classes whilst defining the DomainFactory. Jan 2, 2023 at 21:03
  • 2
    this is covered by the author Wim Velzebor here and here. The documentation in books like Lightning Platform Enterprise Architecture hasn't caught up to this change. You can engage Wim directly in Github apex-common library
    – cropredy
    Jan 2, 2023 at 22:57
  • 1
    also, check out this comment (by me) here. I'll admit, I haven't converted my legacy code base to the new structure but the principles are good :triggerHandlers are triggerHandlers and domains are domains rather than being domains+ triggerHandlers
    – cropredy
    Jan 4, 2023 at 0:39
  • @cropredy you should turn these comments into an answer...
    – Phil W
    Jan 4, 2023 at 8:00

1 Answer 1

1

The documentation in books like Lightning Platform Enterprise Architecture hasn't caught up to this change

Here's a summary (I'll use Opportunity as the example)

Books/videos/trailhead/etc

  • Domain class named Opportunities:
    • Extended fflib_SObjectDomain
    • Trigger handler for all DML events on Opportunity
    • Optional other methods that could be performed on lists of Opportunity SObjects
    • mockable via ApexMocks for Unit Testing

The domain class is essentially a wrapper around a collection of SObjects of a given type - allowing one to perform operations on the collection where the primary use case is handling trigger events but other use cases are easily imaginable.

Latest fflib

The prior domain class Opportunities becomes two classes

  • OpportunityTriggerHandler - extends fflib_SObjectDomain

    • Only serves to perform trigger handling
    • Constructed only by fflib_SObjectDomain.triggerHandler(OpportunityTriggerHandler.class)
    • No newInstance method; not constructed by Application factories
  • Opportunities extends fflib_SObjects

    • Does not do trigger handling
    • Intended to do domain-y stuff on collections of SObjects
    • Can be constructed using the Application Domain factory and hence implicitly supports mock domains using ApexMocks setMock()

Class hierarchy

 fflib_SobjectDomain extends fflib_SObjects extends fflib_Objects
 

You can see this in the apex-common-samplecode github repo by inspecting:

  • Application.cls
  • OpportunityTriggerHandler.cls
  • Opportunities.cls
  • OpportunitiesTrigger.trigger

One possible point of confusion is the XxxTriggerHandler extending fflib_SObjectDomain when the trigger handler is not the domain class any more. I believe, reading through all the PR comments, that this was done for backward compatibility reasons so existing code bases could be upgraded. This allows legacy code bases to retain the dual function domain+triggerHandler logic in a single class and as one adds new triggers, one can adopt the new two-class model (one for triggerHandler, one for domain)

The PR author, Wim Velzebor, makes reference to how one can also construct domains of arbitrary ApexTypes. Such domains would extend fflib_Objects. There is no Application factory support in the sample code for such domains of ApexTypes but one could imagine this.

1
  • Excellent breakdown. Thanks for all your help. Jan 5, 2023 at 11:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .