π Common Patterns β
Practical patterns and best practices for working with bpmn-to-code.
Naming Conventions β
Your element IDs directly shape the generated API. Use consistent prefixes for clean, readable constants:
| Element Type | Pattern | Example |
|---|---|---|
| Tasks | Activity_ | Activity_SendEmail, Activity_ProcessPayment |
| Start Events | StartEvent_ | StartEvent_FormSubmitted |
| End Events | EndEvent_ | EndEvent_OrderCompleted |
| Timers | Timer_ | Timer_After3Days, Timer_EveryMorning |
| Messages | Message_ | Message_OrderReceived |
| Errors | Error_ | Error_InvalidData |
| Signals | Signal_ | Signal_CancellationRequested |
Avoid: generic IDs like Task_1 or Event_abc123.
Explicit Variable Definitions β
bpmn-to-code only extracts variables from explicit variable definitions in the BPMN model β not from expressions in sequence flows, gateways, or script tasks. The supported sources depend on the engine:
| Source | Camunda 7 / Operaton | Zeebe |
|---|---|---|
| I/O mappings | β | β |
| Multi-instance attributes | β | β |
| Call activity in/out mappings | β | β |
additionalInputVariables / additionalOutputVariables extension properties | β | β |
Do: Define variables explicitly in I/O mappings.
<camunda:inputOutput>
<camunda:inputParameter name="subscriptionId">${subscriptionId}</camunda:inputParameter>
<camunda:outputParameter name="mailSent">true</camunda:outputParameter>
</camunda:inputOutput><zeebe:ioMapping>
<zeebe:input source="=subscriptionId" target="subscriptionId" />
<zeebe:output source="=mailSent" target="mailSent" />
</zeebe:ioMapping>Don't rely on variables only referenced in expressions:
<!-- This variable won't appear in the generated API -->
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">
${execution.getVariable('decision') == 'ACCEPTED'}
</bpmn:conditionExpression>Additional Input / Output Variables (Camunda 7 / Operaton) β
Some elements don't support I/O mappings β for example, message start events in Camunda 7 and Operaton. Variables arriving with the triggering message won't be captured by camunda:inputOutput / operaton:inputOutput, so they would be missing from the generated API.
The workaround is to declare them explicitly using two directional extension properties β additionalInputVariables and additionalOutputVariables. Values are routed to the element's Inputs / Outputs sub-object accordingly:
<bpmn:extensionElements>
<camunda:properties>
<camunda:property name="additionalInputVariables" value="orderId, customerEmail" />
<camunda:property name="additionalOutputVariables" value="processingResult" />
</camunda:properties>
</bpmn:extensionElements><bpmn:extensionElements>
<operaton:properties>
<operaton:property name="additionalInputVariables" value="orderId, customerEmail" />
<operaton:property name="additionalOutputVariables" value="processingResult" />
</operaton:properties>
</bpmn:extensionElements>Each comma-separated value becomes a variable in the generated API under the corresponding direction. Works on any BPMN element, not just start events. The legacy undirected additionalVariables property is no longer extracted.
Multi-Environment Modeling β
bpmn-to-code automatically merges BPMN models with identical process IDs into a single API.
dev-order-process.bpmn -> processId="orderProcess"
prod-order-process.bpmn -> processId="orderProcess"
|
v
OrderProcessApi (merged elements from both)Guidelines:
- Use the same
processIdfor all variants of the same process - Keep core process structure consistent across variants
- The generated API contains the superset of all elements across variants
WARNING
If variants define the same element ID with different semantics, behavior is non-deterministic (last-seen wins). Use different process IDs for substantially different process flows.