The Art of Process Modeling: Designing Processes with Activiti

The art of process modeling: using Activiti to design processes

  • Preface
  • Process related
    • define a process
    • start a process
    • Delete a process
  • Method related
    • Create process
    • Verify whether the process model is legal
    • Get the current person’s to-do list
    • Query candidate tasks
    • Delete historical process
    • Get a list of unfinished historical process instances
    • Get a list of completed historical task instances
    • Query historical activity instances based on business ID

Foreword

“Today’s businesses and organizations increasingly rely on process automation to increase efficiency, reduce costs and ensure consistency. In this digital age, process engines such as Activiti and Flowable have become indispensable tools. However, to realize their full potential , you need to have an in-depth understanding of their working principles and best practices. This blog will give you an initial understanding of the activiti process engine, from basic concepts to practical applications, allowing you to navigate the world of process automation with ease.”

Process related

Define a process

repositoryService.createDeployment().addBpmnModel(name, bpmnModel).tenantId("ceshibo").deploy()

[Explanation] Executing the above method will insert data into 3 tables.

  1. ACT_RE_DEPLOYMENT: Stores process deployment information, including deployment ID, name, time, etc.
  2. ACT_GE_BYTEARRAY: Stores process definition files, including files in BPMN, PNG, XML and other formats.
  3. ACT_RE_PROCDEF: Stores process definition information, including process definition ID, KEY, version number, name, deployment ID, XML file name, etc.

[Warning] The above addBpmnModel(name,bpmnModel) adds a BPMN model to the deployment object. It must be noted that the name must end with .bpmn, otherwise ACT_RE_PROCDEF without data will appear.

Start a process

The premise is to obtain the ProcessDefinition and obtain it according to the id of ACT_RE_DEPLOYMENT

Fields that should be included in the business table

ID: An ID that uniquely identifies a process definition.
KEY: The identifier of the process definition, equivalent to the name of the process definition.
NAME: The name of the process definition, used for display in the process designer.
VERSION: The version number of the process definition, used to distinguish different versions of process definitions.
DEPLOYMENT_ID: The deployment ID to which the process definition belongs.
RESOURCE_NAME: The resource name of the process definition, that is, the corresponding BPMN file name.
DIAGRAM_RESOURCE_NAME: The graphic resource name of the process definition, that is, the corresponding flow chart name.
TENANT_ID: The tenant ID to which the process definition belongs. If there is no multi-tenant requirement, it can be empty.

Get process definition entity

// Get the process definition based on the deployment ID
  ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
  .deploymentId(deploymentId)
  .singleResult();
  • Start the process based on the process id, the id of ACT_RE_PROCDEF

    // Variables are process variables, and businessKey is added here, which is the business id, because each process definition corresponds to multiple process instances.
    ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
                ProcessInstance processInstance = processInstanceBuilder
                        .processDefinitionId(startProcessInstanceDTO.getProcessDefinitionId())
                        .variables(processVariables)
                        .businessKey("test01")
                        .businessStatus(BUSINESS_STATUS_1)
                        .start();
    
  • Start a process instance based on the process definition key

    // Because there will be multiple versions of the process instance, the same key will exist. The latest process instance will be taken here.
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process_key", variables);
    

Tables that will be affected after starting the process

  1. ACT_HI_ACTINST: It is a history table that records information about all activity instances, including completed and running activity instances.
  2. ACT_HI_PROCINST: Historical process instance table, which contains process start time, end time, duration, etc.
  3. ACT_HI_TASKINST: Historical task instance information. During the process execution, the task instance is not completed but started, and it will also be inserted into this table.
  4. ACT_HI_VARINST: Variable information of process instance, process variables and task variables. For each variable that changes, a record is generated in the table. A record corresponds to the change history of a variable, including variable name, variable value, modification time and other information.
  5. ACT_RU_EXECUTION: It is a runtime process execution instance table. Each executing process instance corresponds to a piece of data in this table. The data in this table will continue to change as the process is executed, including process start, process node completion, etc. Generally speaking, if a process has ended, the corresponding record will be deleted from the ACT_RU_EXECUTION table. If a process has not yet ended, the corresponding record will remain in the table until the process ends. A process instance will correspond to one piece of data in the ACT_RU_EXECUTION table, but if there are multiple execution processes under the process instance (for example, if there are sub-processes or parallel gateways), it will correspond to multiple pieces of data.
  6. ACT_RU_VARIABLE: Variable information in the executing process instance
  7. ACT_RU_TASK: The runtime information of the current to-do task, including the task ID, name, description, priority, creation time, expiration time, handler, task status and other information. The data in this table will be deleted after the task is completed.

Delete a process

Deleting process instances is divided into two aspects: deleting runtime data and historical data. Deleting runtime data will delete all runtime data of the process instance, including tasks, execution processes, events, etc., and the historical data of the process instance will also be deleted. Deleting historical data is to delete the historical data of the process instance that has ended, including historical tasks, historical variables, etc.

In addition, when using the process engine to delete process instances, you need to pay attention to the following points:

  1. If there are to-do tasks for this process instance, these tasks need to be deleted or completed first.
  2. If the process instance has been associated with information such as attachments, comments, historical records, etc., these associated information need to be cleared first.
  3. If the process instance that needs to be deleted has already generated historical records, the historical records need to be deleted at the same time.
Map<String, Object> map = new HashMap<>();
// This step checks whether the historical process instance ACT_HI_PROCINST has a qualified one. Here is the business key, and what is returned is a single historical process instance.
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
  .processInstanceBusinessKey(bizId).orderByProcessInstanceStartTime().desc().singleResult();
if (processInstance == null) {<!-- -->
  map.put("code", 1001);
  map.put("msg", "The process has not started!");
  return map;
}
String processInstId = processInstance.getId();
// Here you can not check the runtime process instance, but directly query whether endtime is null from the above historical process instance data. If it is not null, it means that the process has ended. You can use processInstance.getEndTime() to obtain it.
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstId).singleResult();
if (pi == null) {<!-- -->
  //This process instance has been completed
  historyService.deleteHistoricProcessInstance(processInstId);
} else {<!-- -->
  // TODO to-do refresh, because it has been deleted and cannot be found, so it is queried first and then pushed after deletion.
  //The following is to delete process instances, do not change the order.
  runtimeService.deleteProcessInstance(processInstId, "");
  historyService.deleteHistoricProcessInstance(processInstId);
}
map.put("code", 1000);
map.put("msg", "Process deleted successfully!");
return map;

Deleting the process will delete all data in the tables affected by the above started process (Current process instance)

Method related

Create process

Deployment deploy = repositoryService.createDeployment().addBpmnModel(bpmnName, bpmnModel).tenantId(tenantId.toString()).deploy();

[Explanation] The returned Deployment corresponds to the ACT_RE_DEPLOYMENT table

Verify whether the process model is legal

ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
ProcessValidator processValidator = processValidatorFactory.createDefaultProcessValidator();
List<ValidationError> validate = processValidator.validate(bpmnModel);

【illustrate】

ProcessValidatorFactory is a factory class used to create process validators in Activiti. The createDefaultProcessValidator() method creates a default process validator instance. The given bpmnModel is then validated using the process validator, returning a List containing error information found during the validation process.

The validation process can help check whether the process definition conforms to the BPMN 2.0 specification, such as checking for the presence of unconnected nodes, invalid expressions, duplicate elements, etc. If there is error information in the verification results, you can make repairs or adjust the process definition based on the error information to ensure its correctness.

Get the current person’s to-do

Task approveTask = taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(userId).singleResult();

【illustrate】

This code snippet is the process of using Activiti’s task service (taskService) to query a to-do task (approveTask) based on a given process instance ID and user ID.

Specifically, the code does the following:

  1. Use taskService.createTaskQuery() to create a task query object.
  2. Use the .processInstanceId(processInstanceId) conditional filter to specify the process instance ID to which the task to be queried belongs.
  3. Use the .taskAssignee(userId) conditional filter to specify the person in charge of the task to be queried (that is, specify the user ID).
  4. Use the .singleResult() method to execute a query and return a single task result.

Query candidate tasks

Task candTask = taskService.createTaskQuery().processInstanceId(processInstanceId).executionId(executionIds)
  .taskCandidateUser(userId).singleResult();

Specifically, the code does the following:

  1. Use taskService.createTaskQuery() to create a task query object.
  2. Use the .processInstanceId(processInstanceId) conditional filter to specify the process instance ID to which the task to be queried belongs.
  3. Use the .executionId(executionIds) conditional filter to specify the execution ID to which the task to be queried belongs (usually it can be empty, unless you want to query a specific execution task).
  4. Use the .taskCandidateUser(userId) conditional filter to specify the candidate user for the task to be queried (that is, specify the user ID).
  5. Use the .singleResult() method to execute a query and return a single task result.

Based on the query conditions, the purpose of this code snippet is to obtain the tasks that can be claimed by a specific candidate user in a process instance (if they exist).

Delete historical process

List<ProcessInstance> processInstanceList = this.runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(bizKey).list();
if (CollUtil.isNotEmpty(processInstanceList)) {<!-- -->
  for (ProcessInstance pi : processInstanceList) {<!-- -->
    //Here, first delete the running ones and then delete the historical ones.
    this.runtimeService.deleteProcessInstance(pi.getId(), "Due to repeated initiation!");
    this.historyService.deleteHistoricProcessInstance(pi.getId());
  }
}

Specifically, the code does the following:

  1. Use runtimeService.createProcessInstanceQuery() to create a process instance query object.
  2. Use the .processInstanceBusinessKey(bizKey) conditional filter to specify the business key of the process instance to be queried.
  3. Use the .list() method to execute a query and return a list of process instances (processInstanceList).
  4. If the list of process instances is not empty, each process instance is iterated over.
  5. For each process instance, first use the runtimeService.deleteProcessInstance() method to delete the runtime process instance, with the parameters being the process instance ID and the reason for deletion.
  6. Then use the historyService.deleteHistoricProcessInstance() method to delete the corresponding historical process instance, and the parameter is the process instance ID.

Note that bizKey in the above code snippet is an example variable, you need to replace it with a valid business key according to the actual situation.

Get a list of unfinished historical process instances

List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().unfinished().list();

Specifically, the code does the following:

  1. Use historyService.createHistoricProcessInstanceQuery() to create a historical process instance query object.
  2. Use the .unfinished() conditional filter to specify that the historical process instance to be queried must be unfinished.
  3. Use the .list() method to execute the query and return a list of qualified historical process instances (list).

Get a list of completed historical task instances

List<HistoricTaskInstance> hisTaskInstList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstId)
  .finished().orderByTaskCreateTime().asc().list();

Specifically, the code does the following:

  1. Use historyService.createHistoricTaskInstanceQuery() to create a historical task instance query object.
  2. Use the .processInstanceId(processInstId) conditional filter to specify the process instance ID to which the historical task instance to be queried belongs.
  3. Use .finished() conditional filtering to specify that the historical task instances to be queried must be completed.
  4. Use .orderByTaskCreateTime().asc() to specify ascending order by task creation time.
  5. Use the .list() method to execute the query and return a list of eligible historical task instances (hisTaskInstList).

Query historical activity instances based on business ID

// Get historical process instances based on business id
HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(bizKey).orderByProcessInstanceStartTime().desc().singleResult();
// Query completed historical activity instances
List<HistoricActivityInstance> hisActivityInstanceList = ((HistoricActivityInstanceQuery) this.historyService.createHistoricActivityInstanceQuery()
                                                          .processInstanceId(hpi.getId()).finished().orderByHistoricActivityInstanceEndTime().asc()).list();
  • historyService.createHistoricProcessInstanceQuery(): Create a historical process instance query object.
  • processInstanceQuery.processInstanceBusinessKey(bizKey): Set query conditions and query by business ID.
  • orderByProcessInstanceStartTime().desc(): Sort in descending order according to the start time of the process instance.
  • singleResult(): Get a single result, that is, the latest historical process instance object.
  • historyService.createHistoricActivityInstanceQuery(): Create a historical activity instance query object.
  • activityInstanceQuery.processInstanceId(hpi.getId()): Set query conditions and query by historical process instance ID.
  • finished(): Filter to only return completed historical activity instances.
  • orderByHistoricActivityInstanceEndTime().asc(): Sort in ascending order according to the end time of historical activity instances.
  • list(): Get a list of historical activity instances that meet the conditions.