教程:BPMN流程的迁移

教程:BPMN流程的迁移

总览:

在此博客文章中,我们将研究如何使用Flowable迁移BPMN流程实例。

问题:

假设您在生产环境中有一个正在运行的应用程序,并且一切正常。生产几个月后,您的客户希望对流程模型进行一些更改。因为更改对于客户来说是紧急的,所以更改也应应用于所有正在运行的流程实例。因此,您必须考虑迁移路径以更新所有正在运行的流程实例。

用例

介绍

Flowable具有强大的迁移功能,可以迁移BPMN流程。让我们通过一个简单的表保留过程来看看它们。

以下过程模型显示了一个非常简单的餐厅餐桌预订示例。该进程保留一个表,并等待直到保留期结束。时间一到,便会检查客人是否出现在餐厅中,否则该桌子将为其他客人释放。

流程迁移:

开业几个月后,餐厅老板意识到预订过程中存在一些限制。这些限制应在新版本中修复。

例如,如果大多数客人没有及时出现并且他们的餐桌在到达餐厅之前就被释放怎么办?在某些情况下,餐厅已被预订满,他们再也没有免费的桌子了,这对他们来说很沮丧。通过为客人腾出15分钟的额外时间来腾出桌子,可以部分解决此问题。

如果将修改后的流程模型部署到生产中,则只有新收到的预订将获得15分钟的额外时间。为了使更改也适用于使用较旧的流程定义创建的所有正在进行的流程,您必须迁移它们。在这种情况下,可以使用轻松完成。

org.flowable.engine.migration.ProcessInstanceMigrationBuilder.migrateToProcessDefinition(String processDefinitionId)。这种方法可以让你更新的定义IDprocessInstance用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 processInstances = runtimeService.createProcessInstanceQuery().processDefinitionKey(PROCESS_DEFINITION_KEY).list();

 

        // Do migration

        for (ProcessInstance processInstance : processInstances) {

            runtimeService.createProcessInstanceMigrationBuilder()

                .migrateToProcessDefinition(latestProcessDefinition.getId())

                .migrate(processInstance.getId());

        }

    }

}

预定餐桌后,餐厅老板还希望向客户发送确认电子邮件。流程模型将进行如下修改:

与我们在第一个计时器任务之后立即添加第二个计时器任务的第一次更改相比,新的电子邮件任务在第一个计时器之前执行。这意味着,即使我们将所有正在运行的流程迁移到新的流程定义,也不会发送电子邮件,因为它们已经向前了一步(在第一或第二计时器的等待状态下)。要向所有正在运行的进程发送电子邮件,您可以使用的addActivityMigrationMapping方法,该方法org.flowable.engine.migration.ProcessInstanceMigrationBuilder允许您映射两个不同的活动。在我们的用例中,我们希望将处于计时器1(activityId = intermediatetimereventcatching1)或计时器2(activityId = intermediatetimereventcatching2)的等待状态的所有进程移到电子邮件任务(activityId = emailtask1)。

以下代码段向您展示了如何将所有正在运行的流程实例迁移到最新定义并移至电子邮件任务。

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 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());

        }

    }

}

在新添加的电子邮件任务中,从流程实例变量中读取收件人电子邮件地址,因为customerEmail流程实例上没有存储任何变量,但是在迁移期间我们也必须设置该值。

另一个流程更改可能是餐厅员工有机会随时取消预订(已通过解决Signal Boundary Event)。可以通过以下过程模型修改来实现这一新要求:

尽管该流程模型看起来与先前的模型完全不同,但是您不必对先前的代码示例进行任何更改。一旦您迁移了流程,Flowable将自动为新的子流程创建一个Activity实例,这又使以后可以发送“取消”成为可能Signal Boundary Event。

 

我要考虑什么?

如果您打算将流程从definition1迁移到definition2,请牢记活动的ID。例如,如果将a替换为Timer intermediate event a Signal catching intermediate event,则必须将旧的activityId与新的activityId映射。

 

您还需要注意以前的流程实例可能缺少的流程变量,但是新流程定义中需要这些变量。为了防止类似的异常,Unknown property used in expression: ${customerEmail} 您必须在迁移期间显式设置新的过程变量。正如我们在上面的示例中了解到的,可以使用来完成此操作org.flowable.engine.migration.ProcessInstanceMigrationBuilder.withProcessInstanceVariable(String variableName, Object variableValue)。

结论

在此博客文章中,我们将流程实例从旧的流程定义迁移到了新的流程定义。我们学习了如何使用Flowable随附的一些流程迁移功能,以及在进行流程迁移时必须考虑的事项。

迁移示例的实现可从GitHub  https://github.com/flowable/flowable-examples/tree/master/blog/migration-bpmn获取。在那里,您还将找到一个JUnit示例,它可以测试迁移本身。

 

相关教程