Organizing More Complex Workflows

BPMN Model

We’ll be using the following files from spiff-example-cli.

Lanes

Lanes are a method in BPMN to distinguish roles for the workflow and who is responsible for which actions. In some cases this will be different business units, and in some cases this will be different individuals - it really depends on the nature of the workflow. Within the BPMN editor, this is done by choosing the ‘Create pool/participant’ option from the toolbar on the left hand side.

We’ll modify our workflow to get the customer’s payment information and send it to an employee who will charge the customer and fulfill the order.

../_images/lanes.png

Workflow with lanes

To run this workflow

./spiff-bpmn-runner.py -p order_product \
     -d bpmn/tutorial/product_prices.dmn bpmn/tutorial/shipping_costs.dmn \
     -b bpmn/tutorial/lanes.bpmn

For a simple code example of displaying a tasks lane, see Handling Lanes

Subprocesses

Subprocesses allow us to conceptualize a group of tasks as a unit by creating a mini-workflow inside a task. Subprocess Tasks come in two different flavors: expanded or collapsed. The difference between the two types is visual rather than functional.

It also possible to refer to external processes via a Call Activity Task. This allows us to ‘call’ a separate Process (which might be stored independently of the Process we’re implementing) by referencing the ID of the called Process, which can simplify business logic and make it re-usable.

We’ll expand ‘Fulfill Order’ into sub tasks – retrieving the product and shipping the order – and create an Expanded Subprocess.

We’ll also expand our selection of products, adding several new products and the ability to customize certain products by size and style in addition to color.

../_images/dmn_table_updated.png

Updated product list

Note

I’ve added what customizations are available for each product in the ‘Annotations’ column of the DMN table. This is not actually used by Spiff; it simply provides the option of documenting the decisions contained in the table.

Since adding gateways for navigating the new options will add a certain amount of clutter to our diagram, we’ll create a separate workflow for selecting and customizing products and refer to that in our main workflow.

../_images/call_activity1.png

Subworkflow for product selection

We need to make sure the ‘Called Element’ matches the ID we assigned in the called Process.

../_images/top_level1.png

Parent workflow

Running the Model

./spiff-bpmn-runner.py -p order_product \
     -d bpmn/tutorial/product_prices.dmn bpmn/tutorial/shipping_costs.dmn \
     -b bpmn/tutorial/top_level.bpmn bpmn/tutorial/call_activity.bpmn

Example Application Code

Handling Lanes

We are not required to handle lanes in our application, but most of the time we’ll probably want a way of filtering on lanes and selectively displaying tasks. In our sample application, we’ll simply display which lane a task belongs to.

def get_task_description(self, task, include_state=True):

    task_spec = task.task_spec
    lane = f'{task_spec.lane}' if task_spec.lane is not None else '-'
    name = task_spec.bpmn_name if task_spec.bpmn_name is not None else '-'
    description = task_spec.description if task_spec.description is not None else 'Task'
    state = f'{task.get_state_name()}' if include_state else ''
    return f'[{lane}] {name} ({description}: {task_spec.bpmn_id}) {state}'

The tasks lane can be obtained from task.task_spec.lane, which will be None if the task is not part of a lane.

See the Filtering Tasks Section of A More In-Depth Look at Some of SpiffWorkflow’s Features more information about working with lanes in Spiff.