Skip to content

What becomes of a shared value

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

Your workflow aggregate shares values, and something has to store them. This adapter is the one which cannot tell you what. The Process-Engine-API carries a payload, but it says nothing about how an engine behind it keeps that payload. So the honest answer is shorter here than elsewhere, and it ends with a test you have to run yourself.

Every adapter wiki answers the four questions below, in the same order. So you can put two BPMS side by side: Camunda 7 and Camunda 8.

  1. What the platform decided before the BPMS sees anything
    Every adapter gets the values already flattened. Which shapes are left after that.
  2. What this BPMS stores as itself
    What the API promises about a payload (nothing), and what the in-memory mock does.
  3. What your application can say about it
    Why there is no property here, and where the answer is instead.
  4. What it costs to configure nothing
    What you cannot learn from this page, and the cheapest way to find it out.

This page is about the way out, from your aggregate to the engine. The way in is what a @TaskParam of your method reads back. It is described in What a @TaskParam receives. When the values are handed over is described in What reaches the engine.

What the platform decided before the BPMS sees anything

Sharing does not hand your objects to the engine. VanillaBP first flattens what your aggregate exposes. So the payload this adapter sends is already a map of numbers, texts, booleans, lists and maps. Enums, a UUID and the java.time values become text. A nested object becomes a map of its own readable attributes. This rule is written down once, with examples, in What is shared, and when.

That map is the payload of every command this adapter sends. It leaves the adapter exactly as it arrived.

What this BPMS stores as itself

The Process-Engine-API has no variable types. A payload is a Map<String, Object> all the way from the start command to the delivery of a task. This adapter puts the flattened values into that map and converts nothing. There is no serialization format to choose, because this API has no format at all.

What happens next is up to the engine, and the API does not describe it. Take an implementation backed by Camunda 7: a value that engine has no variable type for is stored as an object variable, in Camunda 7's own serialization format. An implementation backed by Camunda 8 keeps its variables as JSON in the broker, and a decimal loses its scale there. The wikis of those two adapters describe both cases; the links are at the top of this page. Neither statement is a promise of this adapter.

The in-memory mock which comes with this adapter is the one case with a fixed answer. It keeps the map it was given and hands back the same objects. So a BigDecimal of 120.50 is still a BigDecimal of 120.50 when a task is delivered. That makes the mock useful for tests. It is also why a test against the mock proves nothing about the engine you will run on.

What this adapter tells the startup check

Since VanillaBP 2, the platform checks the TYPE of every value which travels to the BPMS while your application starts. It refuses a value whose type may not arrive as what it was, unless you declared it. The rule and the property are on Which types may travel.

This adapter gives the shortest answer of the three adapters, and it is the honest one. The Process-Engine-API names no engine and no serialization. So a text, a boolean and an enum are known to survive, and for everything else the answer is "cannot say". That never stops a startup. It does cost you a warning, and the way out of the warning is a test against the engine you really run.

The same check has a second half, about the expressions in your models. This is the one adapter which answers it with nothing. The Process-Engine-API hands over no BPMN model and names no expression language. So the adapter cannot tell an expression from a literal. It also cannot know which places the engine behind the API evaluates. Camunda 7 marks an expression with ${...} or #{...}, and Camunda 8 marks one with a leading =. If the adapter guessed which of the two your engine means, a startup message could say things which are not true. So nothing is reported and no message appears. accept-expressions-in-the-model works here as everywhere, but it never has anything to accept. Entry 29 of GAPS.md says what an engine would have to tell about itself to change this.

What your application can say about it

Nothing through VanillaBP. There is no property here, because it would have nothing to decide. This adapter has no serializer to configure and no client which it could hand a mapper to.

Where the engine behind the API does need to be told something, tell that engine. You provide its beans yourself (see Which engine does it talk to?). So whatever it offers for storing values is configured where you build those beans, not under vanillabp.adapters.<id>.*.

Jackson annotations on a shared class never run, wherever the payload ends up. Take a getter annotated with @JsonProperty("renamedByJackson"): its value travels under the bean name of the getter. The platform builds the map from readable properties and never asks Jackson. This is the flattening of the first section, not something an engine does, so the answer is the same on every BPMS.

What it costs to configure nothing

It costs you the certainty the other two adapter pages give. The engines behind this API differ in just those places: numbers and nested values. This adapter cannot warn you about a loss it never sees.

So measure it once, against the engine you actually run on, before your models depend on it. Share a decimal whose scale matters and a whole number larger than a long. Start a workflow, and read back what that engine holds. Add a nested object if your models navigate one, and look at it the way an operator would. What comes back is the answer for your setup. Write it down where the next person finds it.

Until then, the advice which holds everywhere holds here too. Share less. A workflow aggregate shares the results a model decides on and the values an operator reads. An attribute your models never read needs only @NoSyncWithBPMS, and then it is nobody's problem. And where the digits of a number carry a meaning of their own, share that value as a String. Every engine stores a String as itself.