Skip to content

Choosing a BPMS

Stephan Pelikan edited this page Oct 11, 2026 · 8 revisions

Most applications arrive here with the engine already chosen. Then the adapter for it is all you need. This page is for the other case: you found VanillaBP first, and you have not yet chosen a BPMS (business process management system, the engine which runs your processes).

Your business code does not depend on the choice. What the SPI (service provider interface, the API your code uses) promises is the same on every BPMS, and so is everything else in this wiki. What differs: what the engine can tell VanillaBP, what it costs to operate, and what your BPMN model has to look like. The table compares these, and every cell links the page with the details.

Nothing here is a recommendation. You can pick one and move to another later. That is what BPMS migration is for, so this is a first decision and not a final one.

  1. What differs
    A row-by-row comparison of what each engine can tell VanillaBP and what it asks of your application.
  2. I have a Camunda 7 in production
    What adding VanillaBP to an existing embedded engine means, and what it does not change.
  3. I run a Camunda 8 cluster
    The one decision to take before you build anything, and what follows from the cluster being remote.
  4. I want to start without an engine
    The adapter with no infrastructure at all, and the list to read before it goes into production.
  5. And then?
    Where to continue once the engine is picked.

What differs

Camunda 7 Camunda 8 bpm-crafters Process-Engine-API
Where the engine runs in your application's JVM, on your application's database a remote cluster, SaaS or self-managed behind an API you wire yourself; an in-memory fake by default
What it needs besides the adapter a DataSource and a transaction manager; a serialization format for nested shared values the connection and credentials of a cluster the adapter can search the engine beans of the Process-Engine-API
Maturity of the adapter complete; Camunda 7.24 is pinned and its community edition is end-of-life complete, one release line per cluster minor experimental, mock-first, with a longer deviations list than the others
How a task is delivered the engine's job executor, inside the engine's own transaction; with an engine datasource of its own, in a transaction VanillaBP opens polling job workers, at least once the Task Subscription API, at least once
Identity of a delivery none while the engine shares your datasource, and none is needed there: a redelivery proves nothing was committed. With an engine datasource of its own, the id of the job the job key the task id
Handlers have to be idempotent not for delivery reasons, unless the engine runs on its own datasource and handles user tasks or works outside its transaction yes yes
When the shared aggregate values reach the BPMS inside the engine transaction, before the activity is left after your commit, with the command which completes the task after your commit, with the command which completes the task
So a crash between commit and command cannot leave the two apart leaves the aggregate ahead of the BPMS until the outbox repeats the command same
What becomes of a value the engine has no type of its own for an object variable in the serialization format you configure JSON; a decimal loses its scale, and a whole number above 2^64-1 is refused whatever the engine behind the API does; this adapter cannot say
Can the adapter tell that a workflow is unknown to it yes, exactly, from the engine's own tables yes, by searching the cluster; this is why it needs a cluster it can search no, the answer is always an optimistic yes
Reads lag behind writes no yes, at most workflow-visibility-timeout (10 seconds) possible, depending on the engine behind the API
Keeping workflow modules apart a tenant, prefixed identifiers, or nothing a tenant (needs multi-tenancy enabled), prefixed identifiers, or nothing prefixed identifiers or nothing; a tenant cannot be expressed
Age of a task nobody completes not measured: the engine parks the execution and hands nothing out again measured, and async-task-max-age-action can turn it into an incident measured, and reported
What a model needs so a message correlates nothing; with a correlation id, a local variable at the waiting scope nothing; with a correlation id, the correlation key expression of the subscription nothing at all
What correlating checks before your commit that an execution waits for that message that a deployed model declares the message name nothing
Conditional events yes, and a pushed aggregate evaluates them again do not exist in Camunda 8 whatever the engine behind the API offers
A workflow the BPMS starts itself yes, timer, signal and conditional start events yes, timer and signal start events refused while deploying
The end of a workflow reported as COMPLETED or CANCELED reported as COMPLETED, and as CANCELED from release line 8.10 on not reported
Viewing a workflow the engine's repository and history the query API for the history; for the current version, the definitions this deployment brought what this boot deployed, without history
Version ranges of @WorkflowTask numbers and version tags numbers and version tags tags only, and only of the task at hand
Two adapter ids of that type a datasource of its own, or a table-prefix whose tables already exist a cluster of its own, or one cluster at the price of a search per elected task refused

Three rows are worth reading twice. They decide how much your own code has to carry.

Whether the adapter can tell that a workflow is unknown to it: a migration depends on this row. An adapter which can only guess answers "yes, I have it" for every workflow. So the election never reaches the BPMS behind it, and the operation goes to the wrong engine.

The identity of a delivery: this row decides whether VanillaBP can answer a repeated delivery from its record, or whether your handler runs a second time. See a repeated delivery.

When the shared values reach the BPMS: an operator needs this row when a gateway right behind a service task took the wrong branch. See what is shared, and when.

I have a Camunda 7 in production

Then keep it and add VanillaBP. The adapter runs the plain engine in your JVM and on your datasource, and your existing engine tables stay where they are. Two things to know. First, Camunda 7.24 is the last feature release, and the community edition is end-of-life. So this is a starting point and not a destination, and the migration feature was built for exactly this. Second, operations which move a workflow forward run after your transaction committed, here too, not inside it. The reason: an engine command which loses a concurrency conflict cannot be repeated inside the caller's transaction. The adapter's wiki explains what your code still gets answered synchronously.

I run a Camunda 8 cluster

Then the adapter connects to it. It needs a cluster which it can search. That means secondary storage on the cluster, and credentials which may read what the adapter asks for. A SaaS cluster has both. Everything else follows from the cluster being remote and eventually consistent. Your handlers are called at least once. An operation right after a start waits for the cluster to catch up. And the adapter version carries the cluster minor it was built against (release lines).

I want to start without an engine

The Process-Engine-API adapter is the one with no infrastructure at all. It talks to an in-memory fake unless you wire a real engine behind it. So use it to run a workflow module in a test or in a demo. It is experimental. Read its deviations before anything goes into production on it: no workflow the engine starts itself, no push of a changed aggregate, no history for a viewer.

Every future engine behind the Process-Engine-API will also arrive through this adapter. So a workflow module written against it is not written against a dead end.

And then?

Read the adapter's own wiki. It lists the keys to configure and what your BPMN model has to look like on that BPMS. Its Deviations page is the honest list of what it cannot do, one sentence per gap.

Clone this wiki locally