Skip to content

06) The fflib_SObjectUnitOfWork Class

Coding With The Force edited this page Apr 2, 2021 · 19 revisions

The fflib_SObjectUnitOfWork Class

What is the fflib_SObjectUnitOfWork class?

It is a foundation built to allow you to leverage the unit of work design pattern from within Salesforce. Basically this class is designed to hold your database operations (insert, update, etc) in memory until you are ready to do all of your database transactions in one big transaction. It also handles savepoint rollbacks to ensure data consistentcy. For instance, if you are inserting Opportunities with Quotes in the same database (DML) transaction, chances are you don't wanna insert those Opportunities if your Quotes fail to insert. The unit of work class is setup to automatically handle that transaction management and roll back if anything fails.

If also follows bulkification best practices to make your life even easier dealing with DML transactions.


Why is this class used?

This class is utilized so that you can have super fine control over your database transactions and so that you only do DML transactions when every single record is prepped and ready to be inserted, updated, etc.

Additionally there are two reasons it is important to leverage this class (or a class like it):

  1. To allow for DML mocking in your test classes.
  2. To massively reduce duplicate code for DML transactions in your org.

Think about that last one for a second... how many lines of code in your org insert, update, upsert (etc) records in your org? Then think about how much code also error handles those transaction and (if you're doing things right) how much code goes into save point rollbacks. That all adds up over time to a ton of code. This class houses it all in one centralized apex class. You'll never have to re-write all that logic again.


fflib_SObjectUnitOfWork class method cheat sheet

  1. registerNew() - Registers a single record or list of records as new records that need to be created
  2. registerRelationship(SObject, SObjectField, SObject) - Registers a relationship between one record and another
  3. registerDirty() - Registers a single record or list of records to update
  4. registerDeleted() - Registers a single record or list of records to be deleted
  5. registerEmail(EmailMessage) - Registers an email message to be sent
  6. registerWork(IDoWork) - Registers a callback method to be called after your work has been committed to the database.
  7. commitWork() - Commits your unit of work (records registered) to the database. This should always be called last.

There are many other useful methods in the fflib_SObjectUnitOfWork class, but the above methods are the major ones you'll leverage most frequently.


How to Register a Callback method for an Apex Commons UOW

The following code example shows you how to setup a callback method for your units of work, should you need them.

public inherited sharing class HelpDeskAppPostCommitLogic implements fflib_SObjectUnitOfWork.IDoWork{
    List<Task> taskList;
    
    public HelpDeskAppPostCommitLogic(List<Task> taskList){
        this.taskList = taskList; 
    }
    
    public void doWork(){
        //write callback code here
    }
}

The code below shows you how to actually make sure your unit of work calls your callback method.

fflib_ISObjectUnitOfWork uow = Helpdesk_Application.helpDeskUOW.newInstance();
//code to create some tasks
uow.registerNew(newTasks);
uow.registerWork(new HelpDeskAppPostCommitLogic(newTasks));
uow.commitWork();    

Apex Commons Unit of Work Limitations

  1. Records within the same object that have lookups to each other are currently not supported. For example, if the Account object has a Lookup to itself, that relationship cannot be registered.

  2. You cannot do an all or none false database transactions.

Database.insert(acctList, false);
  1. To send emails with the Apex Commons UOW you must utilize the special registerEmail method.

To do these things in your own way you would need to make a new class that implements the fflib_SObjectUnitOfWork's IDML interface which we'll cover below


How and When to use the fflib_SObjectUnitOfWork IDML Interface

If your unit of work needs a custom implementation for inserting, updating, deleting, etc that is not supported by the SimpleDML inner class then you are gonna want to create a new class that implements the fflib_SObjectUnitOfWork.IDML interface. After you create that class if you were using the Application factory you would instantiate your unit of work like so Application.uow.newInstance(new customIDMLClass()); otherwise you would initialize it using public static fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(new List<SObjectType>{Case.SObjectType}, new customIDMLClass());

Example of an IDML Class

//Implementing this class allows you to overcome to limitations of the regular unit of work class.
public with sharing class IDML_Example implements fflib_SObjectUnitOfWork.IDML
{
    void dmlInsert(List<SObject> objList){
        //custom insert logic here
    }
    void dmlUpdate(List<SObject> objList){
        //custom update logic here
    }
    void dmlDelete(List<SObject> objList){
        //custom delete logic here
    }
    void eventPublish(List<SObject> objList){
        //custom event publishing logic here
    }
    void emptyRecycleBin(List<SObject> objList){
        //custom empty recycle bin logic here
    }
}
Clone this wiki locally