Previous Post:
The previous post introduced a BPMN workflow and the Tasklist app provided by Camunda Platform. You probably realize watching the video in there that it is possible to automate any predictable, and repeatable workflow using Camunda Engine.
In this post, I document the implementation details of automating(using Python) the BPMN workflow below, the same one from the previous post. If you just need fully functioning BPMN Automation example, and refer the github repo.
Now, on to the details.
Camunda 8 is powered by Zeebe - a process automation engine.
To interact with Zeebe, you’d need a client program, and that’s the Zeebe Client, available in several languages. I use Zeebe (Python) Client to implement job workers, which execute specific tasks within a business process.
A job worker typically consists of a Python function that:
Uses the client to poll for and activate jobs of a specific type.
Executes the required business logic (e.g., verifying warranty validity through an API call).
Uses the client again to complete the job successfully or mark it as failed, depending on the outcome of the business logic execution.
For example, here’s how “Verify Warranty Validity” Service Task can be automated using Zeebe Python Client.
import asyncio
from pyzeebe import ZeebeWorker, create_insecure_channel, ZeebeTaskRouter
ZEEBE_ADDRESS = "localhost"
ZEEBE_PORT = "26500"
# Create a TaskRouter to register tasks
task_router = ZeebeTaskRouter()
@task_router.task(task_type="check_warranty_validity")
async def check_warranty(warranty_number: str):
# Always valid hahaha!
return {"warranty_active": True}
async def main():
channel = create_insecure_channel(hostname=ZEEBE_ADDRESS, port=ZEEBE_PORT)
worker = ZeebeWorker(channel)
worker.include_router(task_router)
await worker.work()
if __name__ == "__main__":
asyncio.run(main())
What’s happening is, when you run this piece of code, ZeebeClient starts polling for tasks of “task_type” specified in decorator(check_warranty_validity). When the workflow reaches that “task”, the worker on the client side will run it.
The function "task” used in the decorator is provided by ZeebeTaskRouter as well as ZeebeWorker class but ZeebeTaskRouter helps organize code a lot better. Hence the preference.
For polling to work, there must be a Service Task(or any other executable task) in the BPMN model with the correct configuration. This includes setting the task's type to 'Service Task' and ensuring the task is linked to the appropriate job worker, i.e Job type set to 'check_warranty_validity' in the Camunda properties panel.
For example configuration, see Task definition→Job type highlighted below.
i.e task_type in decorator == Job type in Task definition.
When the workflow reaches that Service Task, that the async python function check_warranty
will be run. If there’re errors, the process stops, and this can be monitored on Camunda Platform. The values returned by this function become part of the Process Variables. For example, if this function returns {“warranty_valid”: True}
, then, you can use it, in another Task from variables_to_fetch
. See zeebe client api for usage.
It is worth stressing that one should avoid creating a job worker that knows too much about other parts of the system or process, as this can lead to high coupling.
Speaking of client worker code, it can be whatever is required for the business logic: A standalone function or a microservice or querying a database. Anything you can call from a Python function will do. Checkout the github repo for worker implementation of the entire model.
That covers one prominent use case of any workflow, right? Meaning, a series of tasks executed one after another.
Next, what about those tasks that happen “outside” that the workflow need to be informed of, like in a “push” manner, in order for it to continue? In my case, it is “Read Response”. I mean, when the service task “Send Email Offering Paid Service” sends an email to customer, I have an either or situation: either wait for the reply, or timeout if no reply arrives in 3 days. To handle such scenarios, BPMN provides Event Based Gateway.
The symbol with a diamond containing a double circle with a hexagon inside - that’s called “Event Based Gateway” - and its behavior is that it waits for specific events (like a message or timer) and then proceeds taking the path of the event that occurs first.
Now, when there’s an email reply from customer, it would reach the “Exclusive Gateway”. When there’s no reply in 3 days, it times out.
However, to inform the engine that the customer has replied, I’ve to let the Engine know, in a “push” manner, and for that ZeebeClient publish_message
can be used, like so:
channel = create_insecure_channel("localhost", 26500)
client = ZeebeClient(channel)
await client.publish_message(
name="response_paid_service",
correlation_key=warranty_number,
variables={"paid_service_ok": bool(customer_response)}
)
warranty_number = "WARR-111"
customer_response = "Yes"
await mock_customer_reply(warranty_number, customer_response)
Again, for a working implementation, see github repo.
Notice that publish_message
takes a “name”, and a “correlation_key”. The “name” parameter refers to the message name defined in the BPMN process. See image below.
The correlation_key, however, is a unique identifier that must be passed for each process execution. In the BPMN model this is the variable name! (listed under “Subscription correlation key” in the image below).
In this example, “warranty_number” is the correlation key. But, as said, you must pass this as a variable with a value, when starting to execute the model!
See github repo. It explains how to start model execution using Camunda Studio.
That covers a second use case. I think these two will be able to cover quite some workflow automation scenarios I can imagine. At least this is all I needed to get the entire workflow automated.
This second “push” scenario that needed custom code, that bothered me a bit as that’s a extra work, right? However that pain is relieved by Camunda Connectors and there’re quite a few.
I’m convinced, any business process, as long as it is predictable, and repeatable, can be modeled using BPMN and automated by Camunda Engine.
Conclusion
Fostering collaboration between stakeholders who define the business process, tech team that implements the automation, and of course, the operations(users that handled actual business process tasks), and continuously monitoring, and adjusting the model, and the automation, I think businesses will be able to experience significant efficiency gains.