Skip to main content

Camunda 7 vs Workflow Engine

info

The author of this article is Denis Kotov.

In this article, we will compare Camunda 7 and Workflow Engine using the course payment process as an example.

Any developer in an enterprise environment have faced the task of automating business processes. There are dozens of ways to deal with this problem and hundreds more to solve it. Today we will talk about specific tools in an applied task.

What is business process automation

Business process automation is often presented through marketing, so it might be difficult to understand what it means. I've encountered two implementations mainly:

  1. Sequential or parallel execution of blocks of code when saving the intermediate state and execution history of these pieces of code (with logic) to the database. The possibility to handle the change of these sequences correctly.
  2. The same thing, but not only for executing blocks of code, and also calling external systems that already implement complex logic. This is called microservice orchestration.

In 100% of cases, visualization of what is happening is required, because the sequence of these blocks is perceived as a business process.

80% require to visualize the change of these blocks without involving programmers.

If visualization and changing pieces of code are not required, then in general, programmers might have several alternatives:

  • Chain of Responsibility Pattern;
  • State machine pattern;
  • Using Petri Nets;
  • Our any if-else and switch-case;
  • Another in-house development variant.

Furthermore, it is worth mentioning BPMN that usually can complement business process automation tasks. BPMN is an XML file format, a list of visual abstractions that are mapped into this XML and how these abstractions interact with each other. If a programmer can load such files into his application and process them correctly, then he will solve the issue of clarity of visualization, because BPMN is popular, well described and has well-established rules regarding style.

An example of a task that BPM engines solve

From the business side, automation tasks often look like this:

It is necessary to automate the process of buying online courses.

Text description:

  • The user selects the course to pay for;
  • The user enters an e-mail;
  • The user clicks "pay";
  • The site displays a modal window with a page from acquiring;
  • The user makes the payment;
  • Acquiring sends payment confirmation;
  • The system sends a notification to the user by e-mail with the details of access to the online course;
  • If the user has not made the payment within 10 minutes after opening the modal window, it is necessary to send the user an email with a promo code.

In BPMN format it will look like this:

BPMN process example

Visualization and changing the sequence of squares through the graphical user interface (GUI) are important here.

Such a problem can be solved by a developer implementing a tool that enables:

  • Visually create a similar diagram;
  • Attach pieces of code to each square;
  • Save the variables of the ongoing process (email, payment amount, promotional code, and so on);
  • Write tests for everything created;
  • Run this in docker or kubernetes;
  • Check that everything is alive on the production environment;
  • Be able to migrate current processes to a new scheme version when it becomes available.

All of this can be done in the usual manner of developers (CI/CD, feature flags, code reviews, and so on).

Choosing the right tool

In this article, I decided to evaluate two tools. One of them is Camunda 7 from JVM world - it is implemented in a variety of projects, but sometimes it may be complicated for users without expertise. The second one comes from the .NET world - it's the opposite to JVM. It turned out that the Workflow Engine is one of the few embedded C# engines.

I've chosen a traditional approach for comparing both tools by considering Use Cases and BPMN support:

  1. Use Case - often engines can be delivered as a standalone container or even a separate application, and sometimes as libraries. As a developer, I prefer the second option because I don't need to overhaul my CI/CD pipelines and can continue to use my existing application elements without the annoying integration with a standalone container.
  2. BPMN support - some engines offer support for BPMN, a formal standard for describing business processes. This standard is very powerful and quite complex. There are dozens of tools that produce a description of business processes in this format, and it would seem that they can be reused. But practice has shown that BPMN itself is always not enough for automation, each specific system will have its own characteristics. For instance, Camunda does not support the cycle element, but BPMN does. Therefore, a statement about the support of BPMN by one or another engine is not a reason to expect that your business processes in this format can be simply and easily automated in it.

Let's compare Camunda 7 and Workflow Engine:

Parameter nameWorkflow EngineCamunda 7
1Programming languageC#, .NETJVM Based - Java, Kotlin etc.
2Integration optionsEmbeddingEmbedding, REST API
3BPMN supportNoYes
4Supported databasesMySQL, Oracle, PostgreSQL, SQL Server (Azure SQL), MongoDBMySQL, Oracle, PostgreSQL, SQL Server, H2, CockroachDB
5DocumentationExtensiveExtensive
6Number of stars on GitHub700+3000+
7CommunitySmallBig
8Horizontal scalingYes, through the databaseYes, through the database
9Framework for testingNoYes
10Administration ApplicationNoYes
11Is there a completely free license option for production?YesYes
12The cost of paid licenses$, one-time$$$$, annually

There is almost no choice between these two technologies - if you have C#, then in general you could use Camunda through the Rest API, but this is significantly time-consuming and more complicated than using Workflow Engine.

If you have a JVM, then you simply cannot use Workflow Engine (you can use a Workflow Server based on Workflow Engine via REST API, but this is a different product).

Let us compare Camunda 7 and Workflow Engine

Start of developing process

Since these two engines are embeddable, the essence of their embedding is the same - you must include libraries from vendors in your application.

Camunda 7 has several libraries - the engine itself, REST API to the engine, web applications for administration. All libraries can be used separately. When adding a dependency, it looks like this (for a typical Spring application):

pom.xml
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
<version>3.2.0</version>
</dependency>

The table in the Camunda database is created by itself.

The Workflow Engine also has several libraries - the engine itself (the REST API for Designer is already built into it), a provider for a specific database. The integration looks like this:

shell
dotnet add package WorkflowEngine.NETCore-Core --version 7.2.0
dotnet add package WorkflowEngine.NETCore-ProviderForMSSQL --version 7.2.0

Or in your .csproj file:

MyProject.csproj
<PackageReference Include="WorkflowEngine.NETCore-Core" Version="7.2.0" />
<PackageReference Include="WorkflowEngine.NETCore-ProviderForMSSQL" Version="7.2.0" />

The database tables can be created by running the SQL scripts that are provided by the vendor.

Create a process diagram

Both tools enable users to model visually processes. Camunda 7 provides a package for all operating systems called Camunda Modeler. Essentially, it can generate a .xml file according to the BPMN standard and send it through the REST-API to Camunda.

Camunda Modeller

The Workflow Engine provides ready-made NPM packages for embedding into various JS frameworks such as Angular or React. And the meaning is the same - the visual scheme is described by XML and sent to the engine through the designer.

Here, of course, I would like to have a ready-made application from the vendor, which is always up-to-date and works properly (It is available for Workflow Server solution, but again, this is a different product).

This is the Workflow Engine Designer interface:

Workflow Engine Designer

The course payment process would look something like this in the Workflow Engine:

Workflow Engine Course process

Basic abstractions and their storage in the database

At this stage, significant differences in the approaches of the two vendors begin by considering advantages and disadvantages.

Camunda tables

There are complex abstractions to implement BPMN in Camunda, judge for yourself:

Camunda tables

  • Process definition - schema in XML;
  • Job definition - specific job to be executed;
  • Process instance - a specific running process;
  • Activity - a square inside a process;
  • Variables - process variables;
  • Job - specific running job;
  • Execution - analogue of a token in Petri nets.

All of this data is also kept for history, including changes to the specified abstractions along with process execution.

Due to the one-to-many approach and the BPMN notation, on which these abstractions are not explicitly highlighted (but are created by Camunda), it can be quite complex to deal with this. To be honest, these abstractions are here for a reason, and this is probably the most direct way to honestly support BPMN.

Workflow Engine tables

In this solution, everything is simpler that in Camunda:

All, the same data is stored for history, including those along with process execution.

Each solution also stores other configuration information (permissions, global values, and so on), but this is not so interesting.

Process abstractions in Camunda

Regarding saving data, it is also important what kind of information exactly is gathered when processes are created. In Camunda, this is a full-fledged, 98% working BPMN, described in the OMG standard. This means that there are about 400 semantic constructs and about 12 key abstractions.

BPMN events

Process abstractions in Workflow Engine

In this solution, everything is quite simple - there are only two main abstractions - Activity and Transition.

Workflow Engine abstractions

Delegating code execution

In both solutions, it is possible to call pieces of your application code. The code and the schema in XML must be somehow connected to do this. In Camunda, this is done by specifying a way to find the code and its unique identifier (in this case, the name of the Bean in the context of Spring):

Code execution in Camunda

This piece of code is implemented in the application itself in this way. Note that the JavaDelete interface must be implemented with the mandatory execute method, which is passed a DelegateExecution object that provides access to everything that we may need from the engine.

package bpmn.payment.delegate

import bpmn.payment.component.EmailSender
import org.camunda.bpm.engine.delegate.DelegateExecution
import org.camunda.bpm.engine.delegate.JavaDelegate
import org.springframework.stereotype.Component

@Component("sendEmailDelegate")
class SendEmailDelegate(val emailSender: EmailSender) : JavaDelegate {
override fun execute(execution: DelegateExecution) {
val email = execution.getVariable("email") as String
val emailId = Integer.parseInt(execution.getVariable("emailId") as String)

emailSender.sendEmail(email, emailId)
}
}

In Workflow Engine it works analogously, but the pieces should be registered manually. In Camunda the registry of unique pieces of code is done by Spring with its DI.

public ActionProvider()
{
// Register your actions in _actions and _asyncActions dictionaries
_actions.Add("MyAction", MyAction); // sync
_asyncActions.Add("MyAsyncAction", MyAsyncAction); // async

_actions.Add("Mamad", Mamad); // sync
_asyncActions.Add("AsyncMamad", AsyncMamad); // async

_actions.Add("Update", Update); // sync
_asyncActions.Add("AsyncUpdate", AsyncUpdate); // async

// Register your conditions in _conditions and _asyncConditions dictionaries
_conditions.Add("MyCondition", MyCondition); // sync
_asyncConditions.Add("MyAsyncCondition", MyAsyncCondition); // async
}

Workflow Engine actions

In the same way, inside our code, we get processInstance and runtime objects, from where we can retrieve all data. An advantage, in the Workflow Engine, a list of registered actions can be gotten, unlike Camuda, where you need to know the exact name of the bean.

On the other hand, Camunda has a built-in runtime executor of conditions on gateways, for instance, the following expression can be written in XML:

Camunda gateway condition

Moreover, it can be validated without additional effort from the development side. Nevertheless, I personally consider that it's bad practice because it's hard to test such expressions, and It may be complicated to understand exactly how variables appear there.

In Workflow Engine, we can declare Conditions in the same way just as an Action. This approach is more explicit and more testable. Conditions can also be added to Transitions:

Workflow Engine transition expression

Besides, you can write code directly in the browser. A test can be written on it, which will might be built into the CI pipeline later, but I don't encourage this.

Writing a Test

Clearly, each of the elements of the process, Action or Bean, can be tested separately. In both solutions, it is considered a good practice not to write big logic inside, and using an interlayer between the engine and the real business logic. Business logic should already be taken out into services or components. This approach will facilitate modularity, usability, and testability.

In addition, I would like to test the process itself and the code. In this case, Camunda provides a library that allows you to raise the engine itself, load diagrams into it, run in-memory database and executing processes step by step by comparing the expected behavior with the one received from the engine.

@Test
@Deployment(resources = ["BPMN/Origination.bpmn"])
fun TechErrorPath() {
// Given
val pi = startProcess()
assertThat(pi).isWaitingAt("Task_0w7obdg")
// When
execute(job())
// Then
assertThat(pi).isWaitingAt("ServiceTask_1sz8j3l")
execute(job())
assertThat(pi).isEnded
}

In Workflow Engine you can use the same approach using MSTest.

Process context

When a process is executed, it retrieves the data. In my example of paying for courses, my context consists of email, selected course, promo code, course cost, and so on. This data needs to be written down somewhere and given access to my pieces of code. In both solutions, you can get them through an object passed to my class - in the case of Camunda, this is DelegateExecution, and in the case of WorkflowEngine, this is processInstance.

In Camunda it looks like this:

@Component("addToSegmentDelegate")
class AddToSegmentDelegate(val emailSender: EmailSender) : JavaDelegate {
override fun execute(execution: DelegateExecution) {
execution.setVariable("paymentStatus", "ok")
val contactId = emailSender.findOrInsertContact(execution.getVariable("email") as String)
val segmentId = Integer.parseInt(execution.getVariable("segmentId") as String)
emailSender.addToSegment(contactId, segmentId)
execution.setVariable("paymentOk", true)
}
}

In Workflow Engine will be:

var stringParameter = processInstance.GetParameter<string>("StringParameter");
var objectParameter = processInstance.GetParameter<SomeCustomType>("ObjectParameter");

A relevant functionality in Workflow Engine is that you can pass your own context provider, which will control how and where such variables are persisted, in Camunda instead such variables are stored in a Named-Value wide table for ALL instances and often becomes a performance problem when fast context growth.

Deploy process diagram

Each solution offer two ways to deploy a scheme - through the API or using the designer (which is essentially the same).

The automatic deployment of XML from project resources, which allows you to store processes in Git and perform procedures for comparing and approving XML versions, it is also available in both tools.

Application launch

All the magic of embedded engines - either with Camunda or with the Workflow Engine, I get a traditional application for a given language and can do whatever I want - wrap a fat jar or exe (or assembling it in the .NET world) into docker, deploy to kubernetes etc. I did not see any problem or peculiarities in these two engines.

Monitoring and operation

Both solutions enable to view the current state of instances, their variables, edit them. In case of Camunda the historical data (i.e. on processes that have completed) is available only in the paid version.

The current state of the instance in Workflow Engine is indicated as follows:

Process status in Workflow Engine

Below the current state of all processes in Camunda 7:

Statuses of processes in Сamunda

Process schema update and instance migration

When changing the version of the process (let's say a process element was added), there are several options in each tool:

  • Do nothing - old processes will be completed as usual, new ones will be initialized from the new version;
  • Migrate instances to the new version.

In Camunda, this is done using a special migration REST API (or an internal API provided by the engine), where should be specified the old and new versions.

In Workflow Engine, we can deprecate obsolete schemas and run automatic migration, which generates events that we can process to implement business logic (enrichment with new data, changing states, and so on):

runtime.OnSchemaWasChanged += (sender, args) =>
{
var pi = runtime.GetProcessInstanceAndFillProcessParameters(args.ProcessId);
if (!pi.ProcessScheme.Activities.Any(a => a.Name == pi.CurrentActivityName))
{
var initialActivity = pi.ProcessScheme.InitialActivity;
pi.CurrentActivityName = initialActivity.Name;
runtime.PersistenceProvider.UpdatePersistenceState(pi,
TransitionDefinition.Create(initialActivity, initialActivity));
}
};

Workload

An important criterion is the method for dealing with loads. Unfortunately, the information regarding this in Camunda and the Workflow Engine, is not so updated.

Overall, in BPM engines is not considered RPS, as a rule is rated the number of instances per second, or operations per second. On a high-loaded server, the Workflow Engine support 250 operations per second. Camunda issues 150-200 Process Instances per second in a similar hardware.

You should keep in mind that if you need about RPS 1000+ from the engine to process handling, then probably these are not "business processes" and you may require another tool, such as Apache Spark or Flink.

Learning curve

Another important consideration is the level of abstraction, including the information review and objects that should be modeled and implemented before using the tool productively. I am involved in this because I have been programming in Kotlin for a long time, and the last time that I wrote in .NET, it was 8 years ago.

Based on the experience, a middle-developer can learn the main principles of Workflow Engine from scratch in around 3-5 weeks. Training from scratch in Camunda and BPMN may take 2-3 months.

Camunda and BPMN are quite complex.

Conclusion

I hope the article helped you make a proper choice and getting deeper into choosing an engine. I have not evaluated all aspects (timers, conditional jumps, working with retries, errors, and so on), but these features are not so important for choosing an engine, and from my point of view both systems are mature in this matter.

The principles to choose are quite simple - if I'm on .NET, then I choose Workflow Engine. If on the JVM, then Camunda (and I'm getting ready for a three-month dive to learn BPMN).

What do you think? What would you use? Which recommendations do you have?