BPMN ⇌ UML Activity Bridge
The kuml-transform-bpmn-to-uml module provides a bidirectional model-to-model (M2M)
bridge between BPMN Process models and UML Activity diagrams. Both share the same
Petri-net / token-flow foundation (ADR-0015), making a structurally lossless bridge
possible for the common subset.
CLI usage
# BPMN → UML Activity
kuml transform membership-process.kuml.kts \
--from bpmn \
--to uml-activity \
--output membership-activity.kuml.kts
# UML Activity → BPMN
kuml transform order-activity.kuml.kts \
--from uml-activity \
--to bpmn \
--output order-process.kuml.kts
--from bpmn / --to uml-activity are sugar for
--transformer bpmn-to-uml-activity and --transformer uml-activity-to-bpmn.
Element mapping
| BPMN | UML Activity | Notes |
|---|---|---|
|
|
Name preserved. |
|
|
One per process. |
|
|
Multiple end events produce multiple |
|
|
|
|
|
Detected by multiple incoming, single outgoing. |
|
|
|
|
|
|
|
|
Marked in metadata; no direct UML equivalent. |
Mixed gateway (multiple incoming AND outgoing) |
|
Shared |
|
Edge |
Roundtrip fidelity
A BPMN → UML Activity → BPMN roundtrip is structurally lossless for the elements in the
table above. The bridge collapses DECISION + MERGE and FORK + JOIN pairs that share
the same "bpmn.sourceId" metadata back into the original MIXED gateway.
What is not preserved:
-
Pool / Lane → ActivityPartition is best-effort. Lane names are recorded in the node metadata key
"uml.partition"and emitted as Kotlin comments. The kUML UML Activity metamodel has no PARTITION node kind. -
BPMN
MessageFlowbetween pools — no UML Activity equivalent. -
Boundary events — not supported by the bridge.
Library API
import dev.kuml.transform.bpmnuml.BpmnUmlBridgeRegistry
import dev.kuml.codegen.m2m.TransformRegistry
import dev.kuml.codegen.m2m.TransformResult
// Register transformers (or rely on ServiceLoader in CLI / Fat-JAR)
BpmnUmlBridgeRegistry.registerAll(TransformRegistry.global())
// BPMN → UML Activity
val result = TransformRegistry.global()
.transform("bpmn-to-uml-activity", bpmnProcess)
when (result) {
is TransformResult.Success -> println(result.script) // kUML DSL script
is TransformResult.Failure -> println(result.message)
}
In a non-CLI context (e.g. a Gradle plugin or an embedded use), call
BpmnUmlBridgeRegistry.registerAll() explicitly — the CLI loads providers via
ServiceLoader automatically.
Example
BPMN input (PdV membership application process):
bpmnModel("PdV Mitgliedsantrag") {
process(id = "p", name = "Mitgliedsantrag") {
val start = startEvent("Antrag eingegangen")
val pruefung = task("Antrag prüfen", type = TaskType.USER)
val gw = exclusiveGateway("Vollständig?")
val nachf = task("Nachforderung senden", type = TaskType.SEND)
val bestaet = task("Bestätigung senden", type = TaskType.SEND)
val ende = endEvent("Abgeschlossen")
start flowsTo pruefung flowsTo gw
gw.flowsTo(bestaet, condition = "Ja") flowsTo ende
gw.flowsTo(nachf, condition = "Nein") flowsTo pruefung
}
}
Transformed UML Activity output:
activityDiagram(name = "PdV Mitgliedsantrag") {
val start = initial("Antrag eingegangen")
val pruefung = action("Antrag prüfen")
val gw = decision("Vollständig?")
val merge = merge()
val nachf = action("Nachforderung senden")
val bestaet = action("Bestätigung senden")
val ende = activityFinal("Abgeschlossen")
flow(start, pruefung)
flow(pruefung, merge)
flow(merge, gw)
flow(gw, bestaet, guard = "Ja")
flow(gw, nachf, guard = "Nein")
flow(nachf, merge)
flow(bestaet, ende)
}
See also
-
BPMN 2.0 DSL — authoring BPMN process models
-
UML DSL — Activity diagram DSL reference
-
Code Generation — other M2M transformers (
uml-to-jpa,uml-to-rest, …)