BPMN Support in Zeebe Through 0.16: A Quick Primer
Zeebe 0.15, released at the beginning of February 2019, shipped with new BPMN features, including message and timer start events and non-interrupting message and timer boundary events.
The Zeebe team has made significant progress on BPMN coverage in the last few releases, and when it comes to the near-term Zeebe BPMN roadmap, the 0.15 features are the last of what’s planned in preparation of a production-ready Zeebe release in mid-2019.
In the Zeebe documentation, we describe each supported BPMN element and its technical specifications. Especially if you’re new to BPMN, we recommend you consult the docs during the modeling process. Many important details are covered, from how to define duration for timer events to workflow instance dataflow.
Because we’ve added many new symbols in the past few months, we also want to write a post with a quick runthrough of the BPMN supported as of Zeebe 0.15 plus some simple guidance on the “business logic case” for each symbol.
Over the course of this post, we’ll introduce elements in a different order than what you see in the docs because we want to make it clear here how elements can build on each other and how combinations of different elements can be used to express complex business logic.
In addition to not being a replacement for the docs, this blog post is also not an in-depth overview of BPMN modeling best practices. That’s something we’ve not yet put together for Zeebe. But stay tuned – it’s on our radar.
If you want to be hands-on throughout the post, we encourage you to download the Zeebe Modeler and do a bit of modeling yourself.
A Few Things To Know About BPMN and Zeebe
First, here’s some background in case you’re brand new to BPMN.
- BPMN workflows in Zeebe are made up of four different types of objects: events (things that happen before, during, or after a workflow), activities (things that must be done in order for a workflow to progress), gateways (decision points used to define the flow), and sequence flows (connectors used to describe the time-logic sequence of events, activities, and gateways).
- BPMN is used to define a graphical model and so-called execution semantics. There’s a 1:1 relationship between the graphical model and executable code – no code generation. In other words, the visual model also exists as an XML file that can be executed directly on an engine such as Zeebe that keeps persistent state of running workflow instances.
- A BPMN workflow is not a directed acyclic graph (DAG), which means it’s possible to model business logic such as looping in BPMN.
- Throughout the post, we’re going to refer to “tokens”. You can think of workflow execution as tokens passing through the workflow model. When a workflow instance is started, a token is spawned at the start of the model. The token then advances with every completed step. When the token reaches the end of the workflow, it is “consumed” and the workflow instance ends. Zeebe’s responsibility is to “drive” the token and to make sure that e.g. jobs are created for worker services when they’re supposed to be, messages received by Zeebe are correlated to a workflow instance, and so on.
For more background on BPMN and why it’s a fit for microservices orchestration, we recommend this blog post.
BPMN in Zeebe
- Start Events
- Intermediate Catch Events
- Message and Timer Boundary Events
- Embedded Subprocess
Every BPMN workflow must begin with a Start Event. In Zeebe, you can choose between:
- None Start Event: There’s no “technical” (message or timer-based) trigger for starting a workflow instance with the None Start Event, which is used to describe some “business event” that signifies when we should start a new instance.
- Message Start Event: Starts a workflow instance when Zeebe receives a message published by some other system. Could be used for workflows that integrate with and react to messages published on e.g. Apache Kafka.
- Timer Start Event: Starts a workflow instance based on some time characteristic that can be expressed in ISO 8601. Could be used for workflows that need to be scheduled on a regular interval.
Tasks in Zeebe are used to represent something that needs to be done before a workflow instance can progress to the next step. There are two different types of tasks supported as of Zeebe 0.15:
- Service Task: A unit of work that needs to be carried out by some external worker; for many Zeebe use cases, the external worker would be a microservice. If you’re familiar with the e-commerce order process we use as an example throughout the docs, you’ve seen the Service Task in a workflow model. The order process contains three different Service Tasks, and the work that needs to be completed at each step could be carried out by three distinct microservices.
- Receive Task: A Receive Task indicates that Zeebe needs to receive a message and correlate it to the correct workflow instance before the token can progress. The Receive Task makes it possible for a workflow instance to wait and then react to a message published by some system separate from Zeebe.
Intermediate Catch Events
Intermediate Catch Events are used to model things that need to happen before our model can progress to the next step (as opposed to modeling work that needs to be done by a microservice, for which we’d use the Service Task). There are two types of Intermediate Catch Events in Zeebe.
- Intermediate Message Catch Event: Similar to the Receive Task, the Intermediate Message Catch Event is used when Zeebe needs to receive a message from an external system before a workflow instance token can progress to the next step. (Later in the post, we’ll explain the difference between the Receive Task and Intermediate Message Catch Event.)
- Intermediate Timer Catch Event: An Intermediate Timer Catch Event acts as a stopwatch. When a workflow instance arrives at the event, a timer is started. When the timer fires (e.g., after a specified interval), the workflow instance progresses according to the sequence flow.
Gateways are used to control the flow of a workflow instance token. A gateway can be used for parallel execution or to specify when a token should only take one of multiple paths. Zeebe currently supports three types of gateways:
- Exclusive (XOR) Gateway: A token will take only one of the outgoing sequence flows from an Exclusive Gateway. Every sequence flow has a condition that is evaluated based on current workflow instance payload. In the example below, a workflow instance will follow a different path depending on whether the total price of items in the order is greater than or less than / equal to $100.
- Parallel Gateway: When a workflow instance token arrives at a parallel gateway, all outgoing sequence flows are followed. Execution of parallel paths continues independently until e.g. another merging parallel gateway. In the example below, we can execute the Process Payment and Fetch Items tasks concurrently, and we won’t progress to the “Ship Package” task until both of these tasks are complete.
- Event-based Gateway: An event-based gateway makes it possible to decide on a sequence flow for a workflow instance token based on events. Each outgoing sequence flow of the Event-based Gateway needs to be connected to an Intermediate Catch Event (message or timer). When a workflow instance token arrives at an event-based gateway, it waits there until the first of the events is triggered. It then takes the outgoing sequence flow of this first triggered event and continues. In the example below, a token will wait at the Event-based Gateway until we correlate the relevant message to the instance or after 60 minutes, whichever comes first.
Message and Timer Boundary Events
Message and Timer Events can also be attached to activities to either a) interrupt the activity to which the boundary event is attached and follow the sequence path out of the boundary event only or b) to create an additional execution without interrupting the activity to which the boundary event is attached.
- Interrupting Boundary Events: Interrupting Boundary Events interrupt the activity to which they’re attached, and a token will follow only the sequence flow out of the attached event. We might attach an Interrupting Message Boundary Event to the “Fetch Items” task in our order workflow and cancel the order as a whole if the customer issues a cancellation request while this “Fetch Items” task is in progress. In this case, the Fetch Items task would not be completed, and the instance token would also not progress to the Ship Package task.
- Non-interrupting Boundary Events: Non-Interrupting Boundary Events create a parallel execution following the sequence flow out of the boundary event without interrupting the initial execution path. We might attach a Non-Interrupting Timer Boundary Event to the “Fetch Items” task in our order workflow and send a delay notification to a customer if this task hasn’t been completed within 48 hours then send another update every 48 hours after that. The token will still follow the “happy path” as modeled, meaning that the “Fetch Items” task will not be canceled. Zeebe supports both Non-Interrupting Message Boundary Events and Non-Interrupting Timer Boundary Events.
Until now, the Receive Task and Intermediate Message Catching Event looked similar in terms of functionality. The ability to attach Boundary Events is a unique feature of the Receive Task. For example, if we haven’t received an expected message within a specified time period, we could trigger an escalation.
A Subprocess is an activity that contains other activities, gateways, events, etc., which itself is part of a larger process. A Subprocess is completely defined inside a parent process, and that’s why it’s often called an Embedded Subprocess. In Zeebe, the primary use case for a Subprocess is to create a new scope for events.
Events that occur during execution of a Subprocess can be caught by a Boundary Event on the boundary of the Subprocess. For a different take on an order cancellation example, we might allow a customer to cancel an order as long as we haven’t yet completed the Ship Package task. We can use an Embedded Subprocess to express this logic.
While quite a lot is possible with the BPMN symbols supported in Zeebe through 0.15, we don’t consider our work on BPMN to be finished. We’re always listening for user feedback regarding symbols to support in the future. If you have input to share, please let us know.
To learn more about BPMN basics, the best place to start is the Zeebe docs. In addition, we’ve written a couple of other blog posts about BPMN and its role in microservices orchestration:
- BPMN and Microservices Orchestration, Part 1 of 2: Flow Languages, Engines, and Timeless Patterns
- BPMN and Microservices Orchestration, Part 2 of 2: Graphical Models, Simplified Sagas, and Cross-functional Collaboration
And be sure to let us know if there are other BPMN-related resources you’d find helpful as you work on your Zeebe project.