apex-enterprise-patterns / fflib-apex-common

Common Apex Library supporting Apex Enterprise Patterns and much more!
BSD 3-Clause "New" or "Revised" License
906 stars 515 forks source link

Add unit of work partial success #319

Closed wspringe closed 2 years ago

wspringe commented 3 years ago

Hey all,

I've added to the unit of work class to allow for a few new things:

Usage

You use this class similarly to SObjectUnitOfWork. The following example all have a bulkified version of their methods as well.

If you want to register a new record to be inserted,

fflib_DmlOperation dmlOperation = unitOfWork.registerNewDmlOperation(record);

if you want to register a record to be updated,

fflib_DmlOperation dmlOperation = unitOfWork.registerDirtyDmlOperation(record);

If you want to register a record to be deleted,

fflib_DmlOperation dmlOperation = unitOfWork.registerDeleteDmlOperation(record);

If you want to register a record to be undeleted,

fflib_DmlOperation dmlOperation = unitOfWork.registerUndeleteDmlOperation(record);

The DmlOperation objects will update by reference once commitWork is called. You can retrieve their information by the fflib_DmlOperationResult objects associated with them. the fflib_DmlOperationResult class utilizes a similar API to the SaveResult classes of apex.

For checking if the DML Operation was a success,

fflib_DmlOperationResult dmlOperationResult = dmlOperation.getDmlOperationResult();
Boolean isSuccess = dmlOperationResult.isSuccess();

The rollback operations require a DmlOperation object in order to rollback the operation. It logically does the opposite DML Operation of whatever it was given. So if an insert DML Operation is given to it, it will register a new delete DML Operation with the unit of work instantiation.

Rolling back insert operation -> registers delete operation Rolling back update operation -> registers update operation with original record Rolling back delete operation -> registers undelete operation Rolling back undelete operation -> registers delete operation

Another thing to note is that the unit of work instantiation will be reset once it commits work. This allows for re-use or the rolling back of records without having to instantiate a new object.

For rolling back an insert operation,

fflib_DmlOperation insertDmlOperation = unitOfWork.registerNew(record);
unitOfWork.commitWork();

fflib_DmlOperation rollbackOperation = unitOfWork.registerRollback(insertDmlOperation);
unitOfWork.commitWork();

Considerations

When using rollback operations, please keep in mind DML Salesforce limits. Rolling back operations will, of course, double the amount of operations that the original registering of work commits.


This change is Reviewable

ImJohnMDaniel commented 2 years ago

@wspringe, thanks for the submission here but it is not required.

If your goal is to setup a partial success UOW utilizing the database.insert(List<SObject>, Boolean) method (and similar methods, then all you need to do is create a new IDML implementation. Here is an example of an AtomicDML class that I use periodically that does a partial success.

public class AtomicDML
    implements fflib_SObjectUnitOfWork.IDML
{
    public void dmlInsert(List<SObject> objList)
    {
        database.insert( objList, false );
    }

    public void dmlUpdate(List<SObject> objList)
    {
        database.update( objList, false );
    }

    public void dmlDelete(List<SObject> objList)
    {
        database.delete( objList, false );
    }

    public void eventPublish(List<SObject> objList)
    {

    }

    public void emptyRecycleBin(List<SObject> objList)
    {

    }
}

There are two overloaded newInstance methods on the Application.UnitOfWork class that take IDML classes as parameters and that is how you would incorporate the partial success IDML class into the UOW.

Please let me know if you have any questions.

cc: @stohn777 and @daveespo