Flowable engineering blog post hero

Engineering

Tutorial: Migration of a BPMN process

AUGUST 7, 2019

Author: Simon Amport

Overview

In this blog post we’ll look at how to migrate a BPMN process instance with Flowable.

Problem

Let’s assume you have a running application in production and everything works fine. After a few months in production your customer would like to have some changes within the process models. Because the changes are urgent for the customer, the changes should also be applied to all running process instances. For this reason you have to think about a migration path to get all running process instances updated.

Use Case

Introduction

Flowable has powerful migration functionalities to migrate your BPMN processes. Let’s look at them by using a simple table reservation process.

The following process model shows a very simple example of a table reservation for a restaurant. The process reserves a table and waits until the reservation is expired. Once the time is over it checks if the guests show up in the restaurant otherwise the table will be released for other guests.

Process migration

A few months after going live the restaurant owner has realised there are a few limitations in this reservation process. Those limitations should be fixed in a new release.

For example, what if most of the guests don’t show up in time and their tables were released before they arrived in the restaurant? In some cases the restaurant was fully booked and they haven’t gotten a free table anymore, which was quite frustrating for them. This problem can be partially solved by giving the guests 15 minutes extra time before the table will be freed up.

If you deploy the modified process model to production, only newly incoming reservations will get the 15 minutes extra time. To get the changes also applied for all ongoing processes which were created with an older process definition you have to migrate them. In this use case that can be easily done by using the org.flowable.engine.migration.ProcessInstanceMigrationBuilder.migrateToProcessDefinition(String processDefinitionId). This method allows you to update the definition id of the processInstance with the latestProcessDefinition.

import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.ProcessDefinition;
 
public class MigrationReservationServiceImpl implements MigrationReservationService {
 
    private final String PROCESS_DEFINITION_KEY = "P001-tableReservation";
 
    private final RepositoryService repositoryService;
    private final RuntimeService runtimeService;
 
    public MigrationReservationServiceImpl(RepositoryService repositoryService, RuntimeService runtimeService) {
        this.repositoryService = repositoryService;
        this.runtimeService = runtimeService;
    }
 
    @Override
    public void migrateProcessInstancesToLatestDefinition(){
 
        // Get the latest process definition
        ProcessDefinition latestProcessDefinition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey(PROCESS_DEFINITION_KEY)
            .latestVersion()
            .singleResult();
 
        // Get all runtime processes
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processDefinitionKey(PROCESS_DEFINITION_KEY).list();
 
        // Do migration
        for (ProcessInstance processInstance : processInstances) {
            runtimeService.createProcessInstanceMigrationBuilder()
                .migrateToProcessDefinition(latestProcessDefinition.getId())
                .migrate(processInstance.getId());
        }
    }
}

The restaurant owner wishes as well to send the customers a confirmation email once the table is reserved. The process model will be modified as follows:

In comparison to the first change where we added a second timer task right after the first timer task, the new email task is executed before the first timer. That means even if we migrate all running processes to the new process definition no email will be sent, because they are already a step further (in a wait state of the first or second timer). To send an email for all running processes as well you can use the addActivityMigrationMapping method of the org.flowable.engine.migration.ProcessInstanceMigrationBuilder which allows you to map two different activities. In our use case we would like to move all processes which are in the wait state of timer 1 (activityId=intermediatetimereventcatching1) or timer 2 (activityId=intermediatetimereventcatching2) to the email task (activityId=emailtask1).

The following code snippet demonstrates you how all running process instances can be migrated to the latest definition and moved to the email task.

import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.ProcessDefinition;
 
public class MigrationReservationServiceImpl implements MigrationReservationService {
 
    private final String PROCESS_DEFINITION_KEY = "P001-tableReservation";
 
    private final RepositoryService repositoryService;
    private final RuntimeService runtimeService;
 
    public MigrationReservationServiceImpl(RepositoryService repositoryService, RuntimeService runtimeService) {
        this.repositoryService = repositoryService;
        this.runtimeService = runtimeService;
    }
 
    @Override
    public void migrateProcessInstancesToLatestDefinition(){
 
        // Get the latest process definition
        ProcessDefinition latestProcessDefinition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey(PROCESS_DEFINITION_KEY)
            .latestVersion()
            .singleResult();
 
        // Get all runtime processes
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processDefinitionKey(PROCESS_DEFINITION_KEY).list();
 
        // Do migration
        for (ProcessInstance processInstance : processInstances) {
            runtimeService.createProcessInstanceMigrationBuilder()
                .migrateToProcessDefinition(latestProcessDefinition.getId())
                .withProcessInstanceVariable("customerEmail", "customer@email.com")
                .addActivityMigrationMapping(ActivityMigrationMapping.createMappingFor("intermediatetimereventcatching1", "emailtask1"))
                .addActivityMigrationMapping(ActivityMigrationMapping.createMappingFor("intermediatetimereventcatching2", "emailtask1"))
                .migrate(processInstance.getId());
        }
    }
}

In the newly added email task the recipient email address is read from the process instance variables, because there was no customerEmail variable stored on the process instances yet we have to set the value as well during the migration.

Another process change could be that the restaurant employees have the chance to cancel a reservation at any time (solved with the Signal Boundary Event). That new requirement can be realized with the following process model adaptations:

Although the process model looks quite different to the previous ones, you do not have to change anything from the previous code example. Once you migrate the process Flowable will automatically create an Activity instance for the new Subprocess, which in turn makes it possible to send the “Cancel” Signal Boundary Event afterwards.

What do I have to consider?

If you plan to migrate a process from definition1 to definition2 keep the ids of the activities in mind. For instance if you replace a Timer intermediate event with a Signal catching intermediate event you have to map the old activityId with the new activityId.

You also need to take care about process variables which could be missing on previous process instances, but are required in the new process definition. To prevent exceptions like Unknown property used in expression: ${customerEmail} you must set the new process variable explicitly during the migration. As we learnt in the example above this can be done with org.flowable.engine.migration.ProcessInstanceMigrationBuilder.withProcessInstanceVariable(String variableName, Object variableValue).

Conclusion

In this blog post we migrated a process instance from an old to a new process definition. We learned how to use some of the process migration features that comes with Flowable and what we have to take into account when we do a process migration.

The implementation of the migration example is available on GitHub https://github.com/flowable/flowable-examples/tree/master/blog/migration-bpmn. There you will also find a JUnit example which tests the migration itself.

Simon Amport

Simon Amport

Share this Blog post
pexels-google-deepmind-18069697
Engineering | FEBRUARY 19, 2024
The Value of AI in Modeling

As AI gains prominence as a pivotal technology and enterprises increasingly seek to leverage its capabilities, we are actively exploring diverse avenues for integrating AI into process automation.

pixabay_egg-583163_1920_stevepb
Engineering | OCTOBER 3, 2023
Low-code, High Impact: Flowable & CMMN for Complex Use Cases

The key to managing complexity is to combine different and multiple tools leads to better, faster, and more maintainable solutions. For example, combining BPMN with CMMN.

AdobeStock_566576699
Engineering | OCTOBER 2, 2023
The New Flowable eLearning Platform

Discover the reasons behind our brand-new Flowable eLearning platform and explore its features by registering for our inaugural free course.