🧭 Analogy
A blueprint isn’t a building. A BPMN diagram describes what should happen; the wiring, plumbing, and fittings — the glue code — make it actually run. The art of executable modeling is connecting the diagram to real connectivity, data handling, and decisions without burying either side in the other.
Three ways to connect model and code
A BPMN model is the skeleton; glue code supplies connectivity (REST, messaging), data transformation, and decisions. There are three conceptual ways to wire a service task to that code:
graph TD M["Service task in model<br/>(logical task type)"] --> PS["1. Publish/subscribe<br/>(preferred)"] M --> RC["2. Referenced code"] M --> PC["3. Prebuilt connector"] PS --> PSd["Glue code subscribes,<br/>does work, completes the job.<br/>Engine is the broker."] RC --> RCd["Engine runs your class<br/>in its thread/transaction.<br/>Simple but coupled."] PC --> PCd["Configure a vendor connector.<br/>Convenient but limited,<br/>proprietary, hard to test."]
- Publish/subscribe (preferred). The model declares a logical task type; glue code opens a subscription and is invoked when an instance reaches that task. The engine itself acts as the broker — no separate message broker needed — and a job is the unit of work the handler pulls and completes (input mapping → real work → output mapping → complete). Often implemented with efficient long polling, so glue code can run in almost any language. Three advantages stand out: you decide if/when to execute (close the subscription if the downstream is down and instances simply wait), independent scaling (or throttling — e.g., an OCR tool licensed for one parallel job), and glue code controls timeouts (long work like video transcoding talks back only when finished).
- Referencing code. The model names a class the engine runs in its own context. Simpler at first, but you are nailed to the engine’s environment, lose temporal decoupling, and — the critical downside — failures depend on the engine, its configuration, and your code, making them hard to investigate.
- Prebuilt connectors. Configure a vendor connector (e.g., an HTTP connector). Convenient but limited to what the vendor foresaw, often harder to test, and proprietary — best for stitching together serverless functions or RPA bots, not a default.
Why pub/sub won
Early projects loved referenced code for its simplicity. But cloud-native trends and polyglot teams made pub/sub the better fit, so modern engines focus on it: it keeps glue code in your stack, in your language, under your control, and decoupled from the engine’s lifecycle.
The model is source code — test it
A process solution behaves like any normal project: check it into version control, build it with your normal CI/CD pipeline, and prefer auto-deploying the model through the microservice’s own deployment path rather than a separate graphical tool. Because the model is source code, unit-test it: simulate the start, assert the instance reaches each expected task, execute jobs with fake behaviour, complete human tasks with test data, and verify outgoing requests against a mocked external system.
Don't test the engine
Process tests should check your model, configuration, glue code, and gateway expressions — not the engine’s internals, and not a full integration. Mock external systems so a process test stays a fast unit test.
Versioning: parallel or migrate
Because processes are long-running, deploying a changed model can’t ignore in-flight instances. Engines keep active instances on their started version and run new instances on the new version. Two strategies:
graph TD
D["Deploy new definition version"] --> Q{"Migrate in-flight instances?"}
Q -->|"no"| P["Run versions in parallel<br/>(legal stability, dev and test)"]
Q -->|"yes"| M["Migrate instances<br/>(patches and bug fixes)"]
P --> PC["Cost: multiple live versions"]
M --> MC["Cost: scripted migration in CI/CD"]- Run versions in parallel — deploy without touching running instances. Use when legal requirements demand a stable procedure once started, on dev/test systems, or when migration is too complex for its payoff. Cost: operational complexity of multiple live versions.
- Migrate instances — move existing instances to the new version, possibly scripted into CI/CD. Use for patches and bug fixes, to avoid juggling versions.
Version glue code and data too
A new model often needs new glue code and new data fields. Make new attributes optional so old instance data still deserializes, then either branch on a versioned code path, make the code check whether new attributes are set (migration-safe), or run an upgrade script to adjust old instance data.
Ownership and the “Model or Code?” line
Analysts draft the what and why; developers make models executable and discover real-world flaws (an API needs an extra parameter) — so they must be empowered to adjust the model, communicating each change with a reason. Ownership of the runnable artifact stays with developers, so no one overwrites technical attributes they cannot see. When deciding what goes in the model versus code, default business logic to code and promote a step into the model only when it must wait, you discuss it regularly, or it crosses a transaction boundary.
See also
- Modeling with BPMN — the notation these models are written in.
- Integrating with services and APIs — what the glue code talks to.
- Where workflow engines fit — the platform that runs and versions definitions.
When to use it — and when not
✅ Reach for it when
- Wiring service tasks to real connectivity, data handling, and decisions
- Choosing between pub/sub, referenced code, and prebuilt connectors
- Evolving a deployed definition while instances are still running
⛔ Think twice when
- Letting analysts own and overwrite the executable artifact's technical attributes
- Testing the engine itself instead of your model, config, and glue code
- Forgetting that a model change may require glue-code and data-version changes
Related topics
BPMN is an ISO-standardized notation that is diagram and source code at once — learn its tokens, tasks, gateways, and events even if you use another language.
auto-foundationsWhere Workflow Engines FitA workflow engine is a persistent, scheduling state machine — run it as a service, keep it decentralized, and judge its adoption by return on investment.
auto-operatingIntegrating with Services and APIsOrchestrate anything with an API — and respect boundaries: synchronous or async calls, correlation, and clean result-only APIs instead of the hot-potato anti-pattern.
Check your understanding
Score: 0 / 41. Which method of connecting model to code does the book prefer for modern engines?
Pub/sub lets you decide if and when to execute, scale glue code independently, and control timeouts in your own code — and works in almost any language via long polling.
2. What happens to running instances when you deploy a new version of a definition?
Engines run multiple versions in parallel. You choose between keeping versions parallel (legal stability, dev/test) or migrating instances (patches and bug fixes).
3. When testing a process, what should you NOT do?
Process models are source code and deserve unit tests — but test your model, configuration, glue code, and gateway expressions, mocking externals; don't re-test the engine.
4. Why must developers own the executable artifact?
Analysts draft the what and why; developers make it executable and must be empowered to adjust it. Ownership of the runnable model stays with developers to protect execution details.
Comments
Sign in with GitHub to join the discussion.