Greetings to Camunda, Zeebe and Dapr!

It’s awesome to see how Dapr and Camunda workflow engine work well together. By using BPMN and DMN we completely remove business logic from the code and only expose APIs to bind calls from Dapr Zeebe binding. The project can be found on GitHub.

Story - the business process

Expecting a sad person having difficulties learning a new language and it would make her happier if someone greets her. But depending on her learning skills (Score Person Learning Curve) she may desire the greeting or not. If her score is more than 30% she receives greeting by email. If less, it generates a business error and sends email to administrator. Yes, cruel world.

BPMN Workflow

BPMN Workflow

Decide how to greet

DMN Business Rules



Since Dapr does all the hard work, we’re left with only few tasks to do. See the full solution on GitHub.

Run infrastructure docker compose to start

Docker Desktop

Using Camunda Modeler upload BPMN workflow to Camunda self-hosted instance

Camunda upload

Have a config file to bind MQTT topic…

kind: Component
  name: greeting-request
  namespace: greetings-camunda
  type: bindings.mqtt
  version: v1
  - name: url
    value: mqtt://host.docker.internal:1883
  - name: topic
    value: camunda/greeting-requested
  - name: consumerID
    value: "{uuid}"
    - greetings-api

Have an API controller with endpoint…

/// <summary>
/// Receiving a message to MQTT pubsub and topic named "camunda/greeting-requested" 
/// and it should start a new Camunda Greet process by invoking zeebe-command binding.
/// </summary>
public async Task<ActionResult> GreetingRequested([FromBody, Required] GreetingRequest request, [FromServices] DaprClient daprClient)

    // serialize and deserialize request to get it into dictionary format.
    var json = JsonConvert.SerializeObject(request, jsonSerializerSettings);

    // start Camunda process by invoking a message to Camunda
    await daprClient.InvokeBindingAsync<PublishMessageRequest, PublishMessageResponse>(Bindings.ZeebeCommand, Commands.PublishMessage,
        new PublishMessageRequest("greeting-requested", request.GreetingId.ToString(), string.Concat("decide-greeting-", request.GreetingId), "10s", // TTL
        JsonConvert.DeserializeObject<Dictionary<string, string>>(json)));

    return Ok();

Build & start APIs

docker compose up -d --build

Run simulation from sim folder

dotnet run 10

Zeebe binding triggers a new process in BPMN workflow in Camunda

Camunda Operate


Throwing business error does not send the ErrorMessage to Camunda engine or at least it’s not accessible in “Send email to admin” task. Hence setting the property explicitly - feels a bit awkward to me.

Whatever I define in Input field is a string. Even though I’d expect it’s Time as on. Then using it in DMN diagram is impossible (or don’t know how) as it always ends with error comparing ValString and ValTime.



Another demo of Dapr and Zeebe (Camunda)

less than 1 minute read

After some time I went back to Dapr (v10) and Zeebe engine used in Camunda Platform 8. Updated projects to .NET 7 and tried again what I learned before and r...

Back to Top ↑


Greetings to Camunda, Zeebe and Dapr!

1 minute read

It’s awesome to see how Dapr and Camunda workflow engine work well together. By using BPMN and DMN we completely remove business logic from the code and only...

Back to Top ↑


Are your class constructors heavy?

3 minute read

One of OOP principles says, that every class should enter valid states only. The bigger class it is, the harder to do. Typically we use class constructors so...

Back to Top ↑


Back to Top ↑