About a month ago we completed a project, where the main user facing features on a CMS based web portal were implemented with help of BPMN. In brief, certain actions at the portal trigger process instances at Camunda. Then processes are used to 1) redirect users to correct user interface routes (User Tasks) 2) pass data to required integrations between steps (Service Tasks).
I’ve written a generic high level summary, but because we ended up with a generic solution enough to re-use this in our future projects, we also released a “presentation” and a Docker demo with code dump to celebrate the annual community day of our CMS of choice.
Here I wanted to write down some technical notes on our BPMN use and why we needed to build additional API layer on top of the existing Camunda REST API:
We had to abstract the separation of running and completed processes away. Regarding the BPMN processes, the frontend fully relies on Camunda for the process state. The frontend needs to know if there has been a recently completed process and whether it completed successfully, was canceled manually or ended with an error. So, we built an abstraction where a single endpoint gives all state we need to know about a specific process, regardless its running state.
You never know if the next User Task is in the current process instance or somewhere deeper in the process tree (or in parallel processes loosely coupled with business key). We ended up with an abstraction where it is practically enough to know a businesskey and any active User Task connected to a process with that business key is returned.
We wanted to limit, which process variables the frontend can write, and it was safer to implement that in the middleware API than at frontent server routes. Technically we used Camunda Modeler “generated forms” feature to specify user tasks fields we allow user to write into. And we do also use submit-form -endpoint at Camunda to enforce validations supported by generated forms.
Finally, we implemented implicit wait for fast external tasks at our “Process API”. Whenever a user task is completed, we check if new (external) service tasks has been introduced into the process. If so, the api waits up to a few seconds for a signal from the external task worker that the task has been completed. This made the user experience fluent, because in most of the cases we are able to redirect user to the next user task route regardless of possible external service tasks between those steps (and when those take longer time, we redirect user to “We are processing your request”-like page that keeps polling the process until the next user task appears).