Add a generic UnitOfWork functionality
This commit is contained in:
@@ -6,6 +6,16 @@ This extension package is dependent on the following packages:
|
|||||||
- [fflib-apex-mocks](https://github.com/apex-enterprise-patterns/fflib-apex-mocks)
|
- [fflib-apex-mocks](https://github.com/apex-enterprise-patterns/fflib-apex-mocks)
|
||||||
- [fflib-apex-common](https://github.com/apex-enterprise-patterns/fflib-apex-common)
|
- [fflib-apex-common](https://github.com/apex-enterprise-patterns/fflib-apex-common)
|
||||||
|
|
||||||
|
## Extensions pack contents
|
||||||
|
These features are offered in this extension package:
|
||||||
|
|
||||||
|
- [**Application class redesigned**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/application-factories) <br/>A Force-DI like feature using dependency injection with the Separation of Concerns design pattern.
|
||||||
|
- [**Persitant Logger**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/Logger) <br/>
|
||||||
|
Ever want to log error messages in a Custom Object? The fflib_Logger will do it for you, even if you have a database-rollback.
|
||||||
|
- [**Extended domain features**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/Domain-Layer) <br/> Tons of extra methods in the domains to access the (S)Objects.
|
||||||
|
- [**Criteria evaluator**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/criteria-based-filter) <br/> Ability to use the same criteria filters for domains and selectors. Build SOQL WHERE clauses in an object-oriented manner. It can also evaluate a list of records against predefined conditions "(1 AND 2 AND (3 OR 4))".
|
||||||
|
- [**Generic UnitOfWork**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/UnitOfWork) <br/> Ever want to execute a bunch of work items in a dynamic manner, and be able to control their order of execution, and run some of it in a separate execution context? Wow... that's a lot to ask.. But, the UnitOfWork will do that for you! <br/> Be ware, this fflib_**UnitOfWork** is something different than the fflib_**SObject**UnitOfwork
|
||||||
|
|
||||||
## Wiki
|
## Wiki
|
||||||
A full overview of the features in this extension pack and how to use them
|
A full overview of the features in this extension pack and how to use them
|
||||||
can be found in [the Wiki pages of this repository](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki).
|
can be found in [the Wiki pages of this repository](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki).
|
||||||
@@ -40,6 +50,7 @@ Some of the changes that have major impact are listed here;
|
|||||||
### October 2021
|
### October 2021
|
||||||
- **Logger functionality** <br/>A replacement of System.Debug that is using Platform events to store log messages in a Custom Object for you to monitor
|
- **Logger functionality** <br/>A replacement of System.Debug that is using Platform events to store log messages in a Custom Object for you to monitor
|
||||||
- **API Upgrade to Winter'22** <br/> All Apex classes are upgraded to API 53.0
|
- **API Upgrade to Winter'22** <br/> All Apex classes are upgraded to API 53.0
|
||||||
|
- **UnitOfWork** <br/> Generic UnitOfWork, capable of executing work items in batch at a single instance. Each item will be executed based on its priority and can be queued in a separate execution context when necessary.
|
||||||
|
|
||||||
### August 2021
|
### August 2021
|
||||||
**Added criteria based on formula; (1 OR 2) AND 3)**
|
**Added criteria based on formula; (1 OR 2) AND 3)**
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* File Name: fflib_DoWork
|
||||||
|
* Description: Abstract class containing generic functionality to order, prioritise and define an execution context for the work item
|
||||||
|
*
|
||||||
|
* @author architect ir. Wilhelmus G.J. Velzeboer
|
||||||
|
*
|
||||||
|
* Copyright (c), W.G.J. Velzeboer,
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above author notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the author nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
public abstract class fflib_DoWork implements fflib_IDoWork, Comparable
|
||||||
|
{
|
||||||
|
protected Integer priority = 0;
|
||||||
|
protected Boolean dedicatedQueueable;
|
||||||
|
|
||||||
|
public virtual Integer compareTo(Object compareToObject)
|
||||||
|
{
|
||||||
|
fflib_IDoWork compareTo = (fflib_IDoWork) compareToObject;
|
||||||
|
if (priority == compareTo.getPriority()) return 0;
|
||||||
|
if (priority > compareTo.getPriority()) return -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the work item to be executed in separate UnitOfWork executed in a Queueable context
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_IDoWork enableQueueable()
|
||||||
|
{
|
||||||
|
return enableQueueable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the work item to be executed as Queueable, not combined with other work items
|
||||||
|
*
|
||||||
|
* @param dedicated When true, execute in a dedicated Queueable context.
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_IDoWork enableQueueable(Boolean dedicated)
|
||||||
|
{
|
||||||
|
dedicatedQueueable = dedicated;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the priority of this work item. The higher the number the higher the priority
|
||||||
|
*
|
||||||
|
* @return Returns the priority
|
||||||
|
*/
|
||||||
|
public virtual Integer getPriority()
|
||||||
|
{
|
||||||
|
return this.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true when the work item is intended to run in real-time
|
||||||
|
*/
|
||||||
|
public virtual Boolean isNotQueueable()
|
||||||
|
{
|
||||||
|
return !isQueueable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true when the work item is intended to be executed in separate UnitOfWork executed in a Queueable context
|
||||||
|
*/
|
||||||
|
public virtual Boolean isQueueable()
|
||||||
|
{
|
||||||
|
return (dedicatedQueueable != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true when the work item is intended to be executed as Queueable, not combined with other work items
|
||||||
|
*/
|
||||||
|
public virtual Boolean isDedicatedQueueable()
|
||||||
|
{
|
||||||
|
return (dedicatedQueueable != null && dedicatedQueueable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the priority value of the work item, this is used to sort the work items
|
||||||
|
*
|
||||||
|
* @param priority The priority value, the working with the highest priority value will be executed first
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_IDoWork setPriority(Integer priority)
|
||||||
|
{
|
||||||
|
this.priority = priority;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>53.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
/**
|
||||||
|
* File Name: fflib_UnitOfWork
|
||||||
|
* Description: Unit Of Work executing a batch of work items at a single instance
|
||||||
|
*
|
||||||
|
* @author architect ir. Wilhelmus G.J. Velzeboer
|
||||||
|
*
|
||||||
|
* Copyright (c), W.G.J. Velzeboer,
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above author notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the author nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
public virtual class fflib_UnitOfWork
|
||||||
|
extends fflib_DoWork
|
||||||
|
implements fflib_IUnitOfWork, fflib_IDoWork, Queueable
|
||||||
|
{
|
||||||
|
protected List<fflib_IDoWork> work;
|
||||||
|
protected List<fflib_IUnitOfWork> doNext;
|
||||||
|
|
||||||
|
public fflib_UnitOfWork()
|
||||||
|
{
|
||||||
|
this(new List<fflib_IDoWork>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public fflib_UnitOfWork(fflib_IDoWork workItem)
|
||||||
|
{
|
||||||
|
this(new List<fflib_IDoWork>{ workItem });
|
||||||
|
}
|
||||||
|
|
||||||
|
public fflib_UnitOfWork(List<fflib_IDoWork> workItems)
|
||||||
|
{
|
||||||
|
this(workItems, new List<fflib_IUnitOfWork>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public fflib_UnitOfWork(List<fflib_IDoWork> workItems, fflib_UnitOfWork nextWork)
|
||||||
|
{
|
||||||
|
this(workItems, new List<fflib_IUnitOfWork>{ nextWork });
|
||||||
|
}
|
||||||
|
|
||||||
|
public fflib_UnitOfWork(List<fflib_IDoWork> workItems, List<fflib_IUnitOfWork> nextWork)
|
||||||
|
{
|
||||||
|
this.work = workItems;
|
||||||
|
this.doNext = nextWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single work item is executed in a dedicated Queued context after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @param nextWork work item to be executed after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_IUnitOfWork addNext(fflib_IDoWork nextWork)
|
||||||
|
{
|
||||||
|
return addNext((fflib_IUnitOfWork) new fflib_UnitOfWork(nextWork));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a UnitOfWork to be executed in a Queued context after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_IUnitOfWork addNext(fflib_IUnitOfWork nextWork)
|
||||||
|
{
|
||||||
|
return addNext(new List<fflib_IUnitOfWork>{ nextWork });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an ordered list of UnitOfWork to be executed each in a separate Queued context after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_IUnitOfWork addNext(List<fflib_IUnitOfWork> nextWork)
|
||||||
|
{
|
||||||
|
doNext.addAll(nextWork);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a single work item to the queue
|
||||||
|
*
|
||||||
|
* @param work work item to be added
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_UnitOfWork addWork(fflib_IDoWork work)
|
||||||
|
{
|
||||||
|
this.work.add(work);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a list of work items to the queue
|
||||||
|
*
|
||||||
|
* @param work work items to be added
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
public virtual fflib_UnitOfWork addWork(List<fflib_IDoWork> work)
|
||||||
|
{
|
||||||
|
this.work.addAll(work);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the work contained in the UnitOfWork
|
||||||
|
*/
|
||||||
|
public virtual void doWork()
|
||||||
|
{
|
||||||
|
this.work.sort();
|
||||||
|
doRealTimeWork();
|
||||||
|
doQueuedWork();
|
||||||
|
doNextWork();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the work when being invoked from a queued context
|
||||||
|
*
|
||||||
|
* @param ctx QueueableContext
|
||||||
|
*/
|
||||||
|
public virtual void execute(QueueableContext ctx)
|
||||||
|
{
|
||||||
|
doWork();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do start with the execution of the next work item to be executed in a Queued context
|
||||||
|
*/
|
||||||
|
protected virtual void doNextWork()
|
||||||
|
{
|
||||||
|
if (this.doNext.isEmpty()) return;
|
||||||
|
|
||||||
|
fflib_IUnitOfWork nextWork = this.doNext.remove(0);
|
||||||
|
nextWork.addNext(this.doNext);
|
||||||
|
enqueueJob(nextWork);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put the given UnitOfWork in the Queue for execution
|
||||||
|
*
|
||||||
|
* @param nextWork The UnitOfWork to be executed in a Queued context
|
||||||
|
*/
|
||||||
|
protected virtual void enqueueJob(fflib_IUnitOfWork nextWork)
|
||||||
|
{
|
||||||
|
if (Test.isRunningTest())
|
||||||
|
{
|
||||||
|
nextWork.doWork();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
System.enqueueJob(nextWork);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put the given work item in the Queue for execution
|
||||||
|
*
|
||||||
|
* @param work The work item to be executed in a Queued context
|
||||||
|
*/
|
||||||
|
protected virtual void enqueueJob(fflib_IDoWork work)
|
||||||
|
{
|
||||||
|
if (Test.isRunningTest())
|
||||||
|
{
|
||||||
|
work.doWork();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
System.enqueueJob(work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do start executing the work items marked for execution in a Queued context.
|
||||||
|
* Either in a dedicated Queued context or batched together in another UnitOfWork
|
||||||
|
*/
|
||||||
|
protected virtual void doQueuedWork()
|
||||||
|
{
|
||||||
|
if (this.work.isEmpty()) return;
|
||||||
|
|
||||||
|
List<fflib_IDoWork> workToQueue = new List<fflib_IDoWork>();
|
||||||
|
for (fflib_IDoWork workItem : work)
|
||||||
|
{
|
||||||
|
if (workItem.isNotQueueable()) continue;
|
||||||
|
|
||||||
|
if (workItem.isDedicatedQueueable())
|
||||||
|
{
|
||||||
|
enqueueJob(workItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
workToQueue.add(workItem.enableQueueable(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workToQueue.isEmpty()) return;
|
||||||
|
|
||||||
|
enqueueJob((fflib_IUnitOfWork) new fflib_UnitOfWork(workToQueue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do start executing the work items which are not marked for Queued context.
|
||||||
|
*/
|
||||||
|
protected virtual void doRealTimeWork()
|
||||||
|
{
|
||||||
|
if (this.work.isEmpty()) return;
|
||||||
|
|
||||||
|
for (fflib_IDoWork workItem : work)
|
||||||
|
{
|
||||||
|
if (workItem.isQueueable()) continue;
|
||||||
|
|
||||||
|
doWork(workItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param workItem
|
||||||
|
*/
|
||||||
|
protected virtual void doWork(fflib_IDoWork workItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItem.doWork();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new WorkItemException('WorkItem failed to execute with error: ' + e.getMessage() + ' ' + e.getStackTraceString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WorkItemException extends Exception {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>53.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* File Name: fflib_IDoWork
|
||||||
|
* Description: Interface defining the method signatures for a work item used by the UnitOfWork
|
||||||
|
*
|
||||||
|
* @author architect ir. Wilhelmus G.J. Velzeboer
|
||||||
|
*
|
||||||
|
* Copyright (c), W.G.J. Velzeboer,
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above author notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the author nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
public interface fflib_IDoWork extends Comparable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Do the work of this work item
|
||||||
|
*/
|
||||||
|
void doWork();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the work item to be executed in separate UnitOfWork executed in a Queueable context
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for chaining
|
||||||
|
*/
|
||||||
|
fflib_IDoWork enableQueueable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the work item to be executed as Queueable, not combined with other work items
|
||||||
|
*
|
||||||
|
* @param dedicated When true, execute in a dedicated Queueable context.
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for chaining
|
||||||
|
*/
|
||||||
|
fflib_IDoWork enableQueueable(Boolean dedicated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the priority of this work item. The higher the number the higher the priority
|
||||||
|
*
|
||||||
|
* @return Returns the priority
|
||||||
|
*/
|
||||||
|
Integer getPriority();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true when the work item is intended to be executed in separate UnitOfWork executed in a Queueable context
|
||||||
|
*/
|
||||||
|
Boolean isQueueable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true when the work item is intended to run in real-time
|
||||||
|
*/
|
||||||
|
Boolean isNotQueueable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns true when the work item is intended to be executed as Queueable, not combined with other work items
|
||||||
|
*/
|
||||||
|
Boolean isDedicatedQueueable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the priority value of the work item, this is used to sort the work items
|
||||||
|
*
|
||||||
|
* @param priority The priority value, the working with the highest priority value will be executed first
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for chaining
|
||||||
|
*/
|
||||||
|
fflib_IDoWork setPriority(Integer priority);
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>53.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* File Name: fflib_IUnitOfWork
|
||||||
|
* Description: Interface class with method signatures for the Unit Of Work
|
||||||
|
*
|
||||||
|
* @author architect ir. Wilhelmus G.J. Velzeboer
|
||||||
|
*
|
||||||
|
* Copyright (c), W.G.J. Velzeboer,
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above author notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the author nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
public interface fflib_IUnitOfWork
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Adds a single work item is executed in a dedicated Queued context after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @param nextWork work item to be executed after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
fflib_IUnitOfWork addNext(fflib_IDoWork nextWork);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a UnitOfWork to be executed in a Queued context after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
fflib_IUnitOfWork addNext(fflib_IUnitOfWork nextWork);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an ordered list of UnitOfWork to be executed each in a separate Queued context after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
fflib_IUnitOfWork addNext(List<fflib_IUnitOfWork> nextWork);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a single work item to the queue
|
||||||
|
*
|
||||||
|
* @param work work item to be added
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
fflib_IUnitOfWork addWork(fflib_IDoWork work);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a list of work items to the queue
|
||||||
|
*
|
||||||
|
* @param work work items to be added
|
||||||
|
*
|
||||||
|
* @return Instance of itself to allow for method chaining
|
||||||
|
*/
|
||||||
|
fflib_IUnitOfWork addWork(List<fflib_IDoWork> work);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the work contained in the UnitOfWork
|
||||||
|
*/
|
||||||
|
void doWork();
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>53.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* File Name: fflib_UnitOfWorkTest
|
||||||
|
* Description: Unit Test class for fflib_UnitOfWork implementation
|
||||||
|
*
|
||||||
|
* @author architect ir. Wilhelmus G.J. Velzeboer
|
||||||
|
*
|
||||||
|
* Copyright (c), W.G.J. Velzeboer,
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above author notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the author nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
@IsTest(IsParallel=true)
|
||||||
|
private class fflib_UnitOfWorkTest
|
||||||
|
{
|
||||||
|
public static List<String> orderOfExecution = new List<String>();
|
||||||
|
|
||||||
|
@IsTest
|
||||||
|
static void itShouldDoAllTheWork()
|
||||||
|
{
|
||||||
|
// GIVEN - Three different work items added to the fflib_DoWork
|
||||||
|
Work workA = new Work();
|
||||||
|
Work workB = new Work();
|
||||||
|
Work workC = new Work();
|
||||||
|
fflib_UnitOfWork myWork = new fflib_UnitOfWork().addWork(
|
||||||
|
new List<fflib_IDoWork> {workA, workB, workC}
|
||||||
|
);
|
||||||
|
|
||||||
|
// WHEN - The work is being executed
|
||||||
|
System.Test.startTest();
|
||||||
|
myWork.doWork();
|
||||||
|
System.Test.stopTest();
|
||||||
|
|
||||||
|
// THEN - Then all three work items should be executed
|
||||||
|
System.assert(workA.didWork, 'Work item A did not execute');
|
||||||
|
System.assert(workB.didWork, 'Work item B did not execute');
|
||||||
|
System.assert(workC.didWork, 'Work item C did not execute');
|
||||||
|
System.assertEquals(3, [SELECT COUNT() FROM fflib_LogEntry__c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@IsTest
|
||||||
|
static void itShouldOnlyDoTheRealTimeWorkAndQueueTheOther()
|
||||||
|
{
|
||||||
|
// GIVEN - Three different work items added to the fflib_DoWork
|
||||||
|
Work workA = new Work();
|
||||||
|
Work workB = new Work();
|
||||||
|
Work workC = (Work) new Work().enableQueueable();
|
||||||
|
fflib_UnitOfWork myWork = new fflib_UnitOfWork().addWork(
|
||||||
|
new List<fflib_IDoWork> {workA, workB, workC}
|
||||||
|
);
|
||||||
|
final Integer numberOfQueueableJobs = Limits.getQueueableJobs();
|
||||||
|
|
||||||
|
// WHEN - The work is being executed
|
||||||
|
myWork.doWork(); // Execute without start-stop test due to Salesforce limitation
|
||||||
|
|
||||||
|
// THEN - Then all three work items should be executed
|
||||||
|
System.assert(workA.didWork, 'Work item A did not execute');
|
||||||
|
System.assert(workB.didWork, 'Work item B did not execute');
|
||||||
|
System.assert(workC.didWork, 'Work item C was executed in realtime while is should be queued');
|
||||||
|
System.assertEquals(3, [SELECT COUNT() FROM fflib_LogEntry__c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@IsTest
|
||||||
|
static void itShouldExecuteAllTheChainedQueuedWork()
|
||||||
|
{
|
||||||
|
// GIVEN - Three different work items added to the fflib_DoWork to be executed in a chained queueable
|
||||||
|
Work workA = new Work();
|
||||||
|
Work workB = new Work();
|
||||||
|
Work workC = new Work();
|
||||||
|
fflib_IUnitOfWork myWork =
|
||||||
|
new fflib_UnitOfWork()
|
||||||
|
.addNext(workA)
|
||||||
|
.addNext(workB)
|
||||||
|
.addNext(workC);
|
||||||
|
|
||||||
|
final Integer numberOfQueueableJobs = Limits.getQueueableJobs();
|
||||||
|
|
||||||
|
// WHEN - The work is being executed
|
||||||
|
myWork.doWork(); // Execute without start-stop test due to Salesforce limitation
|
||||||
|
|
||||||
|
// THEN - Then all three work items should be executed
|
||||||
|
System.assertEquals(3, [SELECT COUNT() FROM fflib_LogEntry__c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@IsTest
|
||||||
|
static void itShouldExecuteAllItemsInTheCorrectOrder()
|
||||||
|
{
|
||||||
|
// GIVEN - Three different work items added to the fflib_DoWork
|
||||||
|
Work workA = (Work) new WorkA().setPriority(1);
|
||||||
|
Work workB = (Work) new WorkB().setPriority(2);
|
||||||
|
Work workC = (Work) new WorkC().setPriority(3);
|
||||||
|
fflib_UnitOfWork myWork = new fflib_UnitOfWork().addWork(
|
||||||
|
new List<fflib_IDoWork> {workA, workB, workC}
|
||||||
|
);
|
||||||
|
|
||||||
|
// WHEN - The work is being executed
|
||||||
|
System.Test.startTest();
|
||||||
|
myWork.doWork();
|
||||||
|
System.Test.stopTest();
|
||||||
|
|
||||||
|
// THEN - Then all three work items should be executed
|
||||||
|
System.assert(orderOfExecution.get(0).contains('WorkC'), 'Expected Work C with priority 3 to be executed first');
|
||||||
|
System.assert(orderOfExecution.get(1).contains('WorkB'), 'Expected Work B with priority 2 to be executed second');
|
||||||
|
System.assert(orderOfExecution.get(2).contains('WorkA'), 'Expected Work B with priority 1 to be executed last');
|
||||||
|
}
|
||||||
|
|
||||||
|
private virtual class Work extends fflib_DoWork
|
||||||
|
{
|
||||||
|
public Boolean didWork = false;
|
||||||
|
|
||||||
|
public virtual void doWork()
|
||||||
|
{
|
||||||
|
this.didWork = true;
|
||||||
|
insert new fflib_LogEntry__c(LogLevel__c = 'INFO', Message__c = 'Work got executed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WorkA extends Work
|
||||||
|
{
|
||||||
|
public override void doWork()
|
||||||
|
{
|
||||||
|
orderOfExecution.add('WorkA');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WorkB extends Work
|
||||||
|
{
|
||||||
|
public override void doWork()
|
||||||
|
{
|
||||||
|
orderOfExecution.add('WorkB');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WorkC extends Work
|
||||||
|
{
|
||||||
|
public override void doWork()
|
||||||
|
{
|
||||||
|
orderOfExecution.add('WorkC');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>53.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
Reference in New Issue
Block a user