Data in Process (Part 2)

Designing process data access API

Simon Zambrovski
Holisticon Consultants
5 min readDec 23, 2019

--

© PeterFrankfurt

In the first part of the article, I discussed strategies of loading, storing and serializing data as a payload of processes in the Camunda BPM process engine. I believe that the described decisions are important for your process application design. In the same time, they should be supported or even enforced by the API of process data access. This article focuses on the topic of access to process data, API styles and API construction.

Camunda BPM provides several APIs to read, write and delete process instance payload. These are:

  • (Un-typed) Java Object Value API
  • Typed Java API
  • Expression Language
  • REST API

Since Expression Language API is designed for access from process model directly and REST API for access from remote client, I’ll primarily focus on un-typed and typed Java API.

Un-typed Java Object Value API

The Java API for manipulating process variables in Camunda BPM is the un-typed Java Object Value API. It has been introduced in the very beginning of the product (since Camunda Fox) and is distributed between different components: RuntimeService, TaskService, DelegateTask, DelegateExecution.

It consists of various methods for reading, writing and removing variables (see https://docs.camunda.org/manual/latest/user-guide/process-engine/variables/).

The API is un-typed, because it assumes the process variable value to be a Java Object. By doing so, the API fosters the consumer to know the type. In addition, the method return type is Object and you have to cast the received object to the intended type. Here is an example:

There are no checks of type neither — if your code decides to use the variable order and writes an object of type Integer inside, you need to know this later in the process and handle it correctly.

Typed Java API

An additional Java API exists since Camunda BPM 7.2 and it offers typed read access to variables in process payload. It consists of additional read methods onRuntimeService, TaskService, DelegateExecution and DelegateTask. These methods deliver further information about the variable, if such has been stored. Here is the example of how it should be used:

Typed Java API

It may appear confusing to you, but the Typed Java API is not typed. As you can see, the typed API doesn’t provide type information to the client (the client still needs to know this), but allows wrapping values in Type-Wrapper, in order to store additional metadata, for example:

  • Serialization data format
  • De-serialized form of the variable
  • Transient flag (to support transient variables)

In addition to the described API, Camunda provides a helper facade class which can be passed to and read from various contexts: VariableMap.

This facade allows avoiding unchecked casts on read in own code, but still requires the knowledge of the type by the consumer on every request:

Designing process data access API

As shown in the two previous sections, the Camunda BPM offers a Java API to manipulate process variables. This API provides basic operations, but lacks type safety.

In context of larger process applications this leads to errors during development and refactorings. This shortage can be overcome by providing integration tests which read and write process variables and hence will fail with Class Cast Exceptions if variables are used in a wrong way.

In order to enforce type safe access to process variables and ease the development, a data access API must be built. In general, there are multiple approaches you can choose from:

  • Create business-specific facades encapsulating the entire access to process variables. These facades are used in every place, where you need access to variables and are initialized from the corresponding contexts (Process Execution, User Task, Delegate Task or Delegate Execution). Usually, the code to create a single facade for all contexts is too complex, but you will need to create a facade for every context. Then, prior accessing process data, you need to initialize the facade and then manipulate process data by accessing typed facade methods. Here is an example for the Delegate Task, to clarify the idea:
  • Create generic adapters for every process variable. Those adapters encapsulate the target type, so the consumer doesn’t need to know it during the access. In the following section, I’ll discuss this approach more detailed.

Requirements on generic process variable adapter

In the last five years we repeatedly implemented generic process variable adapters in different customer projects. Finally, we decided to produce an open source library with features best practices and came up with the following requirements:

  • The library provide a way to construct generic adapter for every process variable.
  • The adapter contains variable name.
  • The adapter contains variable type.
  • The adapter can be applied in any context (RuntimeService, TaskService, DelegateExecution, DelegateTask, VariableMap).
  • The adapter offers methods to read, write and remove variable values.
  • The adapter works for all types supported by Camunda BPM. This includes primitive types, object and container types ( List<T>, Set<T>, Map<K,V> ).
  • The adapter supports global / local variables.
  • The adapter support transient variables.

In addition to those functional requirements, we came up with a requirement to the API itself from the perspective of the developer. The following snippet demonstrates the usage in Java.

The declaration of process variables adapter
The usage of process variable adapter in a Java delegate

The idea for the API design is that a statically imported process variable adapter provides methods to adapt to the current context (RuntimeService , TaskService, DelegateExecution, DelegateTask, VariableMap) and the ReadWriteAdapter, which is responsible for operations on process variables. By doing so, we offer uniform API access, independent of any context. By providing specially typed customVariable we are able to support object variables and by providing listVariable, setVariable, mapVariable we support container types (to overcome the problem with Java type erasure).

Summary

In this article, I discussed Camunda Java API for accessing process variables. The provided APIs introduce some drawbacks for developer and endanger rapid development and make refactoring error-prone.

To address that, we developed a library which allows a convenient and developer-friendly type-safe access to process variables of Camunda. It unifies the access providing adapters to any usage context and leverages the API with high-level operators for data manipulation.

We are looking forward to release the first version and will use this as a foundation for further solutions for process variables.

If you are interested in the library, check out the project homepage on Github. Feel free to use it, give us feedback, file issues or even contribute to the library.

--

--

Simon Zambrovski
Holisticon Consultants

Senior IT-Consultant, BPM-Craftsman, Architect, Developer, Scrum Master, Writer, Coach