The Architecture Reference

Auto operating · Process Automation · Intermediate

Integrating with Services and APIs

Orchestrate anything with an API — and respect boundaries: synchronous or async calls, correlation, and clean result-only APIs instead of the hot-potato anti-pattern.

Auto operating Intermediate ⏱ 5 min read Complete

🧭 Analogy

A conductor does not play the instruments — they cue the violins, the brass, a guest soloist, and even a recorded track, each on their own. A workflow engine orchestrates the same way: it cues software, a decision table, a human, an RPA bot, or a device, as long as each exposes something it can call.

Orchestrate anything with an API

In process automation, orchestration means coordination (not the cloud-native sense of container management). Because the engine drives tasks, and tasks can call anything with an API, it can orchestrate:

  • software — microservices, methods, or serverless functions;
  • decisions — a DMN business rule task delegating to a decision engine;
  • humanshuman task management, where a user task appears in a tasklist (assign to groups, not individuals, and lean on the built-in task life cycle for reminders and escalation rather than modeling them);
  • RPA bots — for legacy apps with no API, a bot automates the GUI for one service task;
  • devices — IoT, where a stream processor derives an insight that starts a process.

Real processes mix all of these — and the architecture style (microservices, serverless, modular monolith) shapes deployment but not the engine’s value.

Sync, async, and correlation

sequenceDiagram
participant P as Process instance
participant Pay as Payment service
P->>Pay: charge (command, correlationId=UUID)
Note over P: instance waits<br/>(state in DB, no thread blocked)
Pay-->>P: paid (echoes correlationId)
Note over P: correlate by UUID,<br/>resume the right instance
P->>P: timer: if no response in 1 day, escalate
  • Synchronous request/response (typically REST) is modeled as a service task the process waits at. It hides complexity — the remote service may be slow or down — which quickly forces your own service to become long-running.
  • Asynchronous request/response removes temporal coupling but forces you to handle “what if the response never comes” with a timeout timer.
  • Correlation matches an incoming async response to the right waiting instance. Use a fresh artificial ID (a UUID) stored in process variables; avoid engine IDs like process-instance IDs (they can change across restarts or versions); be careful with business data (one order ID split into two payments can’t be correlated distinctly).

The hot-potato anti-pattern

A payment service without persistent state that hits a problem can only throw it back to its client — leaking internal concepts (credit-card retry logic, expired cards) into its API and increasing coupling. Give the service long-running capability so it fully owns its responsibility (email the user, wait a week) and exposes a clean success/failure API. Whether to surface an error to the client should be a conscious business decision, not an accident of missing state.

Respect boundaries: call activity vs API call

A process model is domain logic owned by exactly one bounded context. How processes communicate determines coupling:

  • Within a boundary, a BPMN call activity can directly invoke another process — the parent waits for the child, which can raise error/escalation events. It’s a handy shortcut for reuse, but the engine is the API: both sides must use BPMN on the same engine, true only inside one boundary.
  • Across a boundary, use a normal API call (REST, SOAP, messaging). The caller need not know an engine is involved, so you stay free to change the implementation behind a backward-compatible API.
graph TD
Q{"Caller and callee in the same boundary?"} -->|"yes"| CA["BPMN call activity<br/>engine is the API, same engine both sides"]
Q -->|"no"| API["Normal API call<br/>REST, SOAP, or messaging"]
CA --> R["Handy reuse, tight coupling to one engine"]
API --> F["Free to change implementation<br/>behind a backward-compatible API"]

Avoid the process monolith

A model that embeds another context’s internal details is a process monolith — it can’t be owned by anyone, every relevant change forces multi-team meetings, and ubiquitous-language conflicts pile up. Cut the end-to-end process into per-service pieces, each owned by one team, collaborating through clean APIs. This is favored by Fowler’s “smart endpoints and dumb pipes” — keep smarts in the services, not a central spider.

RPA, decisions, and devices

RPA is a stopgap, not a platform

An RPA bot is “the macro recorder on steroids” — brittle GUI automation for systems with no API. Use it for one service task, keep a human task as fallback for bot errors, and plan to replace it with a real API (often without changing the model). Never let RPA own the core process; that just imports low-code’s disadvantages.

Externalize fast-changing decisions

Decision logic changes far faster than control flow. Express it as a DMN decision table with FEEL, invoke it from a business rule task, and the business can change rules without touching the process. The decision’s audit info shows up in the instance history.

See also

When to use it — and when not

✅ Reach for it when

  • A process must coordinate software, decisions, humans, RPA bots, or devices via APIs
  • A service should own a long-running responsibility and expose a clean API
  • Choosing how a process crosses a boundary (API call) versus reuses within one (call activity)

⛔ Think twice when

  • Building a process monolith that embeds another context's internal details
  • Using a call activity across a service boundary (it couples both to one engine)
  • Letting an RPA bot own the overall process instead of one screen-automation step

Check your understanding

Score: 0 / 4

1. What can a workflow engine orchestrate?

Orchestration here means coordination. Real processes mix software calls, DMN decisions, human tasks, RPA bots for legacy GUIs, and IoT devices — all reachable via APIs.

2. What is the 'hot potato' anti-pattern?

Without long-running capability a stuck service can only throw the problem back to its caller, exposing internals like credit-card retry logic in its API. Statefulness lets it own the responsibility and expose a clean result-only API.

3. When is a BPMN call activity appropriate?

A call activity makes the engine itself the API, so both sides must use BPMN on the same engine — true only inside one boundary. Crossing a boundary must be a normal API call (REST/SOAP/messaging).

4. What should RPA be used for?

RPA bots are brittle GUI automation — one bot per service task, with human fallback for errors. Prefer a real API and plan to swap the bot out, often without changing the model.

Comments

Sign in with GitHub to join the discussion.