Flow Elements
A FlowElement describes a process in your application. It allows you to define a number of states and state-transitions which describe the flow of this process.
A FlowElement always targets a DataElement with a status field. At runtime, each instance of this DataElement will then be processed to follow the defined state transitions. Each transition can be associated with a TaskElement, which will be executed on the instance being processed.
space
└── model
├── space.xml
├── dataElements
└── flowElements
├── FlowElement1.xml
└── FlowElement2.xml
<!-- FlowElements should be named in PascalCase -->
<flowElement name="FlowElement1">
<packageName>net.demo</packageName>
<!-- targetClass points towards the packageName + name of the target DataElement -->
<targetClass>net.demo.MessageBus</targetClass>
<!-- statusField points towards the field in the target DataElement
that should be used to keep the state of this WorkFlow -->
<statusField>status</statusField>
</flowElement>
There used to be a requirement to define the status field starting with uppercase. However, through the power of modern science, this is no longer necessary.
Required DataElement Configuration
The target DataElement requires some configuration to use it with a FlowElement
Status Field
The DataElement requires a field which will be used to keep the state of the workflow.
This field should have the following characteristics:
- It should be a ValueField with type
String
- It should have the option
isStatusField
The convention is to name this field status, however, other names are also possible.
It is also required to declare a Finder for this field, e.g. findByStatusEq
or findByStatusSe
.
Example:
<dataElement name="Starship">
<fields>
<field name="status">
<fieldType>VALUE_FIELD</fieldType>
<valueField>
<valueFieldType component="" name="String"/>
</valueField>
<options>
<isStatusField/>
</options>
</field>
</fields>
<finders>
<finder name="findByStatusSe">
<fieldOperatorPairs>
<fieldOperatorPair name="Se:status">
<operator>Se</operator>
<field name="status"/>
</fieldOperatorPair>
</fieldOperatorPairs>
</finder>
</finders>
</dataElement>
Data States
DataStates represent the different states a DataElement can have.
When defining a FlowElement, you should also define the States in the DataElement:
<dataElement name="Starship">
<dataStates>
<dataState name="Initial"/>
<dataState name="Processing"/>
<dataState name="ProcessingFailed"/>
<dataState name="Processed"/>
</dataStates>
</dataElement>
As the Workflow becomes more complex, you will start to introduce more states. It's best to have a different failed state for each transition, which improves traceability.
FlowElements and Component Boundaries
It is possible to create a FlowElement that targets a DataElement from another Component by adding a ComponentDependency.
However, this introduces some issues related to Separation of Concerns:
- First, the DataElement now contains a status field related to a FlowElement of another Component. This Field is tightly coupled to the FlowElement and thus breaks the unidirectional behavior of the dependency.
- Second, since the DataElement and it's FlowElement often both play part in the same functionality, there might be a problem with functionality that is implemented over multiple Components. This often means that the Components were split for technical reasons, not because of clearly separated domains.
A solution to this could be to introduce a new DataElement in the same Component that acts as a proxy for the other DataElement and keeps the state. Or you might need to rethink the design of your Components.
Multiple FlowElements for a Single DataElement
Having multiple FlowElements use the same DataElement is possible, if they use different fields as status fields.
However, there are some question you should be asking yourself before doing this:
- Am I adding too much complexity to one DataElement? Perhaps there should actually be multiple DataElements?
- Am I, apart from the status field, introducing additional Fields for this FlowElement? If so, doesn't that mean those Fields represent a missing concept in the Model?
- Are the Workflows resistant against concurrency effects? If 2 workflows process the same instance at the same time, it is possible for one to override the changes of the other.
If you want to prevent multiple processes from overriding each others changes, consider adding a Field with the option isVersion
.
This will introduce a optimistic locking mechanism to the DataElement.