Skip to content

πŸ“– 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 TypePatternExample
TasksActivity_Activity_SendEmail, Activity_ProcessPayment
Start EventsStartEvent_StartEvent_FormSubmitted
End EventsEndEvent_EndEvent_OrderCompleted
TimersTimer_Timer_After3Days, Timer_EveryMorning
MessagesMessage_Message_OrderReceived
ErrorsError_Error_InvalidData
SignalsSignal_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:

SourceCamunda 7 / OperatonZeebe
I/O mappingsβœ…βœ…
Multi-instance attributesβœ…βœ…
Call activity in/out mappingsβœ…β€”
additionalInputVariables / additionalOutputVariables extension propertiesβœ…β€”

Do: Define variables explicitly in I/O mappings.

xml
<camunda:inputOutput>
  <camunda:inputParameter name="subscriptionId">${subscriptionId}</camunda:inputParameter>
  <camunda:outputParameter name="mailSent">true</camunda:outputParameter>
</camunda:inputOutput>
xml
<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:

xml
<!-- 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:

xml
<bpmn:extensionElements>
  <camunda:properties>
    <camunda:property name="additionalInputVariables" value="orderId, customerEmail" />
    <camunda:property name="additionalOutputVariables" value="processingResult" />
  </camunda:properties>
</bpmn:extensionElements>
xml
<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.

TIP

See the engine pages for full details: Camunda 7 Β· Operaton

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 processId for 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.