Skip to main content

General Overview

The process automation component is designed to gradually increase complexity when needed. This section will take you through the most common functionality of the component

A step by step guide on how to create a workflow, can be found in the Developer Hub:


Prerequisites​

It is advised to read up on these topics if they are not immediately clear yet.

  • Component model: We will extend the Component model with Workflows.
  • Finite state machines: Processing is modeled using state machines. We will refer to state machine terminology such as state and transition.

Creating a workflow​

When using Process Automation, you will mostly be working with the Workflow model, which is an extension of Component. This means it lives next to (and not inside) DataElement and TaskElement. Each workflow defines a single state machine. Most applicationsApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. are too complex to put into a single state-machine, which is why multiple state machines can run in parallel.

Selecting a target​

The state within our state machine needs to be stored somewhere. Since data is modeled using DataElements, they serve as the target for workflows.

components/mycomp/workflows/MyElementFlow.xml
<workflow>
<name>MyElementFlow</name>
<packageName>net.demo</packageName>
<targetField>mycomp::MyElement::status</targetField>
<states/>
<transitions/>
</workflow>

Marking the initial state​

A workflow defines a set of states, which lists all possible states in our state-machine. Each state has a name property to make it easier when reasoning about a workflow. Additionally one state can be marked as the initial state. As a result of defining the initial state, every instance from the targeted DataElement will start with a status value equal to the name of the initial state. This value is set in the pre-create anchorAnchorA marker within generated source code reserved for adding custom logic during re-expansion. on the «DataElement»Bean.

components/mycomp/workflows/MyElementFlow.xml
<workflow>
<name>MyElementFlow</name>
<states>
<state>
<name>Initial</name>
<initialState>true</initialState>
</state>
</states>
</workflow>
caution

A state machine should always have exactly one initial state. Read the documentation on initial states to see how having multiple initial states can be prevented.

Transitions within workflow​

Transitions are defined to model functionality within a workflow. A workflow transition however is slightly different from a classical state-machine transition. A workflow transition in fact defines three or more transitions. The main components are a begin state, interim state, failed state and end state. The state machine of a single workflow transition is depicted below.


info

You may wonder why we define an interim state. The primary reason is to facilitate a separation of state, it becomes immediately obvious when a dataElement is being processed by a workflow, and when it is finished. A second more technical reason is related to locking. Enterprise applicationsApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. have a great concurrency model, but we do not want to concurrently modify the same object. By eagerly modifying the state to an interim state, we can avoid such modifications.

components/mycomp/workflows/MyElementFlow.xml
<workflow>
<name>MyElementFlow</name>
<states>
<state>
<name>Initial</name>
<initialState>true</initialState>
</state>
<state>
<name>Making</name>
</state>
<state>
<name>FailedToMake</name>
</state>
<state>
<name>Made</name>
</state>
</states>
<transitions>
<transition>
<name>Make</name>
<beginState>mycomp::MyElementFlow::Initial</beginState>
<interimState>mycomp::MyElementFlow::Making</interimState>
<failedState>mycomp::MyElementFlow::FailedToMake</failedState>
<endStates>
<endState>
<state>mycomp::MyElementFlow::Made</failedState>
</endState>
</endStates>
</transition>
</transitions>
</workflow>

Task Execution​

Each unit of work is modeled as a TaskElement which makes it the ideal integration with workflows for task execution. To execute a task within a transition, the executeTask fieldFieldA property of a DataElement expanded to a database column and user interface input element. needs to be configured.

One thing to consider is the that the targetField on a workflow must be from the same DataElement as the TaskElement targetElement. The task will contain any business logic needed for the transition. Below is the updated version of the previous example, where the only two results the task can have are a TaskResult.success and a TaskResult.error.

Branching tasks​

With tasks as discussed so far it is already possible to model many use-cases for a workflow. By introducing branching the model can become even more expressive. A TaskElement allows you to define a set of TaskOutcomes. Each outcome can then be interpreted by a transition in order to determine the correct end state. Consider the example of Schrödingers cat and a task to open the box. The following is a more concrete example of the diagram shown earlier.


Since the TaskOutcomes are decoupled from the resulting state, it is possible to re-use (branching) Tasks in multiple transitions.

components/mycomp/tasks/ValidateMyElement.xml
<taskElement name="ValidateMyElement">
<targetElement component="mycomp" name="MyElement"/>
<outcomes>
<taskOutcome name="Valid"/>
<taskOutcome name="Invalid"/>
</outcomes>
</taskElement>
components/mycomp/workflows/MyElementFlow.xml
<workflow>
<name>MyElementFlow</name>
<states>
<state>
<name>Initial</name>
<initialState>true</initialState>
</state>
<state>
<name>Validating</name>
</state>
<state>
<name>ValidationFailed</name>
</state>
<state>
<name>MyElementValid</name>
</state>
<state>
<name>MyElementInvalid</name>
</state>
</states>
<transitions>
<transition>
<name>Validate</name>
<executeTask>mycomp::ValidateMyElement</executeTask>
<beginState>mycomp::MyElementFlow::Initial</beginState>
<interimState>mycomp::MyElementFlow::Validating</interimState>
<failedState>mycomp::MyElementFlow::ValidationFailed</failedState>
<endStates>
<endState>
<state>mycomp::MyElementFlow::MyElementValid</state>
<taskOutcome>mycomp::ValidateMyElement::Valid</taskOutcome>
</endState>
<endState>
<state>mycomp::MyElementFlow::MyElementInvalid</state>
<taskOutcome>mycomp::ValidateMyElement::Invalid</taskOutcome>
</endState>
</endStates>
</transition>
</transitions>
</workflow>

Modeling behavior​

In order to tell a workflow when tasks should be performed, there is the concept of Triggers which model the behavior of transitions. Process automation ships with several useful triggers out of the box: OnCreate, OnModify, OnTransition, ByDataCommand, Schedule and FlowEngine.

Creating triggers is very similar regardless of its type.

components/mycomp/workflows/MyElementFlow.xml
<workflow>
<name>MyElementFlow</name>
<transitions>
<transition>
<name>Make</name>
<!-- other fields omitted for brevity -->
<triggers>
<trigger type="workflows::OnCreate">
<name>on:create</name>
</trigger>
<trigger type="workflows::FlowEngine">
<name>flow:engine</name>
</trigger>
</triggers>
</transition>
</transitions>
</workflow>

OnCreate​

It is not uncommon for dataElements to require some pre-processing after they are created. The OnCreate trigger is perfect for such scenario. The result of adding this trigger is that a transition is immediately added to the processing queue when a dataElement is created, causing a near instant transition as a result. This should only be used on transitions with the initial state as their begin state.

OnModify​

Very similar to the OnCreate trigger, but is activated when a DataElement is modified. This is useful when additional processing or validation is required after a user has changed the element in some way.

OnTransition​

Another event based trigger. OnTransition will activate after each transition within the workflow. This makes it suitable for chaining multiple transitions together. It is recommended to keep Task implementations as small as possible, by using this Trigger the induced overhead of multiple sequential Transitions is minimal.

ByDataCommand​

We often see certain transitions originate in user interaction. To facilitate this, the ByDataCommand trigger integrates execution of a transition within a command.

In order for this to work, we first need to define a dataCommand. This command has two important options to customize behavior. By default, the command will only schedule the transition and return immediately. To make the command wait while transitioning, the workflow.trigger.blockingMs option can be either set to a positive integer (the timeout) or to the value until_complete (no timeout). The options workflow.trigger.button can be used to conveniently add a button to the knockout UI, if present in your applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution..

components/mycomp/dataElements/MyElement.xml
<dataElement name="MyElement">
<dataCommands>
<dataCommand name="trigger">
<hasTargetInstance value="true"/>
<options>
<workflow.trigger.button/>
<workflow.trigger.blockingMs>500</workflow.trigger.blockingMs>
</options>
</dataCommand>
</dataCommands>
</dataElement>
tip

The ByDataCommand trigger is an abstraction well suited for integration-testing workflow transitions. Its independence on time or other (uncontrollable) events makes it great for swift testing without working around stateful execution.

Schedule​

Some actions may be bound to time. For example sending out messages at a specific time or requesting data from an external service at regular intervals. The Schedule trigger is added to achieve this using a cron-like schedule. The default values in the schedule result in "Every day at 00:00 local time".

fieldFieldA property of a DataElement expanded to a database column and user interface input element.defaultexample
second0*/30 "every 30 seconds"
minute0*/5 "every 5 minutes"
hour01,13 "twice a day at 1 'o clock"
dayOfWeek*Tue,Fri "every tuesday and friday"
dayOfMonth*1 "first of every month"
month*1,2 "only in January and February
year*2025 "only in 2025"
timezoneJVM timezoneEurope/Amsterdam

FlowEngine​

Primarily added to ease the migration from the 'workflow' component to process automation. Results in a single interval timer per workflow which fetches all elements which are in the beginState of any transition with the FlowEngine trigger.

The main difference is that this will only perform transitions of the current state, and not traverse the whole transition graph. If you need this behavior, configure the follow-up transitions with the OnTransition trigger.

There are various settings to tweak the behavior of FlowEngines, which are documented on the triggers page.

info

FlowEngines have a limited batch size unless configured otherwise. In case the begin- and endState are equal, you may need to define a sorting such that each element is picked up eventually. Use the flowEngine.sortField option to configure this sorting.

Option
flowEngine.sortField Field

Let flowEngines on this element sort their batches by the fieldFieldA property of a DataElement expanded to a database column and user interface input element. this option applies to. Sorting direction can be specified through the value and defaults to asc. FieldsFieldA property of a DataElement expanded to a database column and user interface input element. annotated with the audit.modify.timestamp option will automatically be added to the sortFieldsFieldA property of a DataElement expanded to a database column and user interface input element..

<options>
<flowEngine.sortField/>
</options>
<options>
<flowEngine.sortField>(asc|desc)</flowEngine.sortField>
</options>

Transition recovery​

It is possible a transition cannot complete successfully. As a result a DataElement may be stuck in an interim state. To resolve this issue process automation attempts a recovery procedure at startup. The default behavior is to move to the failed state.

Recovery state​

Alternatively, the recovery state can explicitly be configured on the Transition. Simply select a recovery state from the dropdown to configure it as the state to be used when recovering the transition.

components/mycomp/workflows/MyElementFlow.xml
<workflow>
<name>MyElementFlow</name>
<transitions>
<transition>
<name>Make</name>
<!-- other fields omitted for brevity -->
<recoveryState>mycomp::MyElementFlow::MakeFailed</recoveryState>
</transition>
</transitions>
</workflow>

Transactions​

It is possible to use the begin state as the recovery state with one restriction. The executed task must be transactional. In order for recovery to the begin state to be valid, the task must not have performed any side effects. Having a transaction around a task at least ensures there were no database modifications as a result of a failed task.

Note

You still need to make sure no other side effects were performed within the task. For example when making a request to an external api, a compensating request may be needed in order to cancel the initial request.