Browse documentation
Docs/Language

Routing BPMN instances with DMN decisions

Import an eligibility policy and a bounded process together. Evaluate the policy once when creating an instance, route that instance to fulfillment or review, inspect the decision and firing…

Routing BPMN instances with DMN decisions

Import an eligibility policy and a bounded process together. Evaluate the policy once when creating an instance, route that instance to fulfillment or review, inspect the decision and firing explanations, and retain its marking for replay or cancellation. The workflow uses ordinary native DECISION and NET definitions through the existing compiler, inference and LIR execution.

Import and run

The explicit Rust API is grid_format_standard::bpmn::import_with_decision(bpmn, dmn, binding, context). DecisionBinding names the exclusive gateway, the supplied decision ID, and a map from outgoing flow IDs to distinct scalar output values. No resource is fetched and no external task runs. The maintained local adapter prints source:

cargo run --offline --manifest-path rust/Cargo.toml -p grid-format-standard --example bpmn-decision-to-grid -- examples/workflows/order-routing.bpmn examples/workflows/order-routing.dmn examples/workflows/order-routing-binding.json > /tmp/order-routing.grid
cat examples/workflows/order-routing.grid >> /tmp/order-routing.grid

Compile the emitted source normally, or load it as a module using USE. The example policy emits "eligible" for amounts 0 through 100 and "review" above 100. The binding associates "eligible" with the process's fulfill flow. Its explicit BPMN default flow is review. A negative amount has no decision result and creates no instance.

accepted = bpmn_begin(40)
initial = MATCH(accepted, OK(m) -> m, ERR(e) -> e)
started = MATCH(bpmn_net.FIRE(initial, :start), OK(r) -> r.next, ERR(e) -> e)
explanation = bpmn_explain(started, :route_1)
routed = bpmn_net.FIRE(started, :route_1)

bpmn_begin takes inputs in DMN input-column order. It returns OK(marking) only after a valid, present decision result selects a route. The marking's parameters.admission retains the exact inputs, native decision report, route ordinal and source flow ID. Subsequent firings read this frozen route; they do not reevaluate the policy or read live workbook inputs. A new invocation creates an independent instance. Use bpmn_begin as the checked entrypoint; the emitted dmn_table and parameterized bpmn_net.INITIAL are internal implementation surfaces and do not establish the combined import contract by themselves.

Closed admission contract

bpmn-2-dmn-exclusive-v1 reuses the DMN scalar profile and BPMN task profile, with these explicit differences:

Construct Admitted meaning
Decision Exactly one supplied DMN decision with one scalar output; existing FIRST, UNIQUE and ANY semantics
Graph One start, one end, plain tasks, exactly one exclusive split and one exclusive merge; acyclic, 3–8 nodes and 2–8 flows
Split One incoming flow; one guarded transition per outgoing flow advances to exactly one selected position
Merge One outgoing flow; a separate transition accepts each incoming branch independently; it does not wait for unchosen branches
Binding Every non-default split flow has one distinct value of the declared DMN output type
Default Optional BPMN default references an outgoing flow with no value binding; used only for a present decision value matching no explicit route

This exclusive-only profile encodes its single active position as a bounded count in the NET position place. Equality guards make positions distinct; a larger count cannot satisfy an earlier step. Manifest flow count and task activeCount fields map positions back to source. ready, completed and cancelled retain their ordinary terminal counts. This compact representation avoids the deeper parallel-place expansion; it does not change D3.

Bindings admit bounded strings, booleans and exact safe JSON integers. Equality is case-sensitive and type-preserving; string payloads are treated as opaque text. Duplicate values, mismatched types, wrong decision/gateway IDs, missing bindings and unknown flow references refuse at import. The graph's degree and acyclicity checks ensure each branch reaches the sole merge and end.

General conditionExpression/FEEL expressions on flows, parallel/exclusive mixing, nested choices, loops, service/business-rule tasks, events, subprocesses, external actions and implicit scheduling remain outside this profile. XML never silently discards unsupported semantics. The existing parallel-only importer continues to refuse exclusive gateways. This is an explicit two-document binding profile, not full BPMN/DMN interoperability or conformance.

Each document keeps its original parser limits. Combined input bytes, including the binding, are capped at 384 KiB and the caller's byte budget; accounted output is capped at 1 MiB and the caller's output budget. Literal strings are at most 4096 bytes. Import cancellation is checked through the existing Context.

Failure and explanation

Outcome Result of bpmn_begin
Invalid DMN input ERR with BPMN_DECISION_INPUT
Decision conflict or failed decision outcome ERR with BPMN_DECISION_FAILED, retaining its native cause and report
No decision result ERR with BPMN_DECISION_NO_RESULT; default routing does not invent a result
Present value with no matching route and no default ERR with BPMN_ROUTE_NO_MATCH

bpmn_explain(marking, :action) combines the retained inputs, decision report, selected source flow ID and ordinary NET firing report. bpmn_import retains both exact source documents, their source spans, DMN rule-to-native-label maps, BPMN flow/action maps and the explicit binding. Follow matched_rules through the decision mapping, then flow_id through the process mapping. Native reports remain proposals, not commits. Count-NET REPORT uses ordinary formula execution; its existing frozen-callable admission restriction remains unchanged.

Complete, cancel and reopen

The replay completes the chosen task, merges its branch and reaches the terminal token. An unchosen branch, duplicate route or premature completion is disabled without partial effects. cancel drains all ready, flow and active tokens, retaining the admission record. No transition completes work after cancellation. Cancellation does not interrupt an external service, compensate side effects, or assert that physical work stopped.

Persist the ordinary native marking together with its emitted source or compiled LIR. Reopen retains the input and decision report without XML interpretation. NET admission binds both exact XML documents and the binding into the definition identity through a constant initial expression. Changing the policy, binding or process produces a different definition and rejects an old marking; keep the old definition to continue old instances. There is no automatic migration. No compiler/runtime path or default host capability changes are required.

The explicit service-task owner can bind one mapped task to an authorized read-only PROCESS operation with durable results, bounded retry, cancellation and recovery. The importer itself still dispatches nothing.