Skip to content

2. BPMN Modelling Guide

BPMN 2.0 is a diagram standard that the engine can execute. This guide takes the basics in BPMN and Workflow Engines further: the full element set, the patterns used most, and how to model so others can read it. Running example: a small order process with service tasks and error events (the same shape as the simple models used when trying Camunda).

The order process

flowchart LR
    S(("Order received")) --> A["Check inventory"]
    A --> B["Charge payment method"]
    B --> C["Ship items"]
    C --> E(("Order shipped"))
    A -. "error: item not in stock" .-> N(("Order not shipped"))
    B -. "error: payment failed" .-> N
BPMN element Zeebe job type Worker does
Service task "Check inventory" check-inventory Looks up stock
Service task "Charge payment method" charge-payment Calls the payment provider
Service task "Ship items" ship-items Creates the shipment
Error boundary event error code (e.g. out_of_stock) Worker throws a BPMN error; the engine follows the boundary path

Each service task has a task definition type; any worker subscribed to that type picks up the job (Writing Job Workers).

Elements you will use

Events (circles)

Event Start Intermediate (catch / throw) Boundary (on a task) End
None yes yes (a milestone) n/a yes
Message process starts on a message wait for / send a message task interrupted by a message send a message
Timer start on a schedule wait for a duration or date escalate after 3 days n/a
Error n/a n/a catch an error thrown inside end with an error
Signal broadcast start wait / broadcast react to a broadcast n/a
Escalation n/a throw notify without stopping n/a
Terminate n/a n/a n/a stop everything now
  • Interrupting boundary events cancel the task; non-interrupting ones (dashed) run alongside it (for example, send a reminder but keep waiting).
  • Event-based gateway waits for the first of several events (a reply message or a timeout).

Tasks (rounded rectangles)

Task Meaning Who does it
User task A person completes it, usually with a form Tasklist or your own UI
Service task Automated work A job worker or connector
Send / receive task Send or wait for a message Worker / engine
Business rule task Evaluate a DMN decision (DMN and FEEL) Engine
Script task A short FEEL expression Engine
Call activity Run another process Engine
Manual task Done offline, no system involved (documentation only)

Gateways (diamonds)

Gateway Behaviour
Exclusive (X) Exactly one outgoing path, the first whose condition is true; always add a default
Parallel (+) All paths at once; a second parallel gateway joins them
Inclusive (O) Every path whose condition is true
Event-based Wait for the first event to happen

Containers

  • Embedded subprocess: group steps, scope variables, and attach one boundary event to the whole group. In the HZV example the whole "In Quarantine" loop can be cancelled by one timer.
  • Event subprocess: a mini-process that starts when an event occurs anywhere inside its parent (for example "cancel order").
  • Call activity: reuse a process (a "credit check") from many parents.
  • Multi-instance: run a task for every item in a list, in parallel or in turn.
  • Pools and lanes: who does what; the engine executes one pool.

Patterns that come up constantly

1. Wait for a person, escalate if late

flowchart LR
    S(("Start")) --> U["Review request"]
    U --> E(("Done"))
    U -. "timer P3D, non-interrupting" .-> R["Remind manager"]

2. Retry loop with a limit

flowchart LR
    A["Call external service"] --> G{"Succeeded?"}
    G -- yes --> OK(("Done"))
    G -- no --> W["Wait PT10M"]
    W --> C{"Tries left?"}
    C -- yes --> A
    C -- no --> M["Manual review"]
Prefer job retries with back-off in the worker for technical failures. Model the loop only when the business wants a visible retry.

3. Parallel work, then continue

flowchart LR
    S(("Start")) --> F{"+"}
    F --> A["Check credit"]
    F --> B["Verify identity"]
    A --> J{"+"}
    B --> J
    J --> D["Decide"]

4. Run for each item (multi-instance) A service task with a multi-instance marker, input collection =order.items, input element item, and an output collection that gathers each result.

5. Compensation (undo) If shipping fails after payment, a compensation or refund path reverses the payment. Model the undo explicitly. Check your Camunda version's list of supported elements for the exact compensation features.

6. Start by message, correlate later messages A message start event creates the instance; later messages find their instance through a correlation key (for example =order_id). See Writing Job Workers.

7. Timers ISO 8601 values: PT30M (30 minutes), P2D (2 days), R3/PT10M (repeat 3 times every 10 minutes), a fixed date-time, or a FEEL expression such as =quarantine_duration (see the HZV example in the tutorial).

Modelling style: make it readable

Do Don't
Name tasks verb + object: "Check inventory", "Approve claim" Vague names ("Process", "Step 2")
Name events as something that happened: "Order received", "Payment failed" Reuse the same name for different things
Label exclusive gateways as questions: "In stock?" and label every outgoing flow ("yes"/"no") Leave conditions on unlabeled lines
Flow left to right, one main "happy path" in a straight line Crossing lines everywhere
Show end states that mean different outcomes separately ("Shipped", "Cancelled") One end event for everything
Keep a model to roughly a screen; extract subprocesses or call activities One huge diagram
Keep logic in workers and rules in DMN; keep the diagram to what happens when Complex code or conditions in sequence flows
Version and review models like code Edit production models without a trail

Anti-patterns - Spaghetti with lines jumping back (loops without a named reason). - Everything in one pool with no lanes, so nobody knows who owns a step. - Gateways that do work: a gateway only chooses a path. - Business data hidden in variables nobody documents: write down the variables each task reads and writes. - Using the engine as a database: store big data elsewhere and pass references (Best Practices).

Variables and data

  • Variables live on the process instance (and on scopes such as subprocesses).
  • Tasks can map inputs and outputs so each task sees only what it needs.
  • Conditions and mappings use FEEL expressions starting with =: =order.total > 1000, =customer.country = "ZA". Details: DMN and FEEL.
  • Keep payloads small: IDs and key facts, not documents.

Reading the XML (when you need to)

<bpmn:serviceTask id="check" name="Check inventory">
  <bpmn:extensionElements>
    <zeebe:taskDefinition type="check-inventory" />
  </bpmn:extensionElements>
</bpmn:serviceTask>

<bpmn:boundaryEvent id="out_of_stock" name="Item not in stock" attachedToRef="check">
  <bpmn:errorEventDefinition errorRef="Error_out_of_stock" />
</bpmn:boundaryEvent>
The zeebe: parts are Camunda 8 additions: job type, called decision, form, message correlation, input/output mappings. They live in the same .bpmn file as the picture, so one file is both documentation and program.

Tools

Tool Notes
Camunda Desktop Modeler Free app, edit BPMN, DMN and forms, deploy and start instances (Running Camunda 8 Locally)
Camunda Web Modeler In the browser, collaboration, versions, connectors
bpmn.io The open-source modeler library behind both; also works for viewing
Mermaid flowcharts Good for quick sketches in these notes, but not executable BPMN