Mastering Activiti: Build Scalable Workflow Engines in Minutes
This article walks you through designing, deploying, and managing a multi‑level approval workflow using the Activiti engine, covering BPMN diagram creation, Spring Boot integration, database schema, API usage, code examples, and best practices for extending processes without additional code.
Why Use a Workflow Engine?
Workflow approval is a core capability of office OA systems, involving multiple users, task handoffs, and branching. Without a powerful engine, extending or adding new processes becomes cumbersome and error‑prone.
Activiti simplifies this by handling process execution and branch decisions automatically.
Creating a Simple Leave Approval Process
Step 1: Design the BPMN Diagram
Use the ActiBPM plugin for IntelliJ IDEA to draw a diagram that models a two‑level approval: first‑level supervisor approves, and if the leave exceeds three days, a second‑level supervisor approves.
First‑level supervisor approval
Second‑level supervisor approval (if >3 days)
Deploy the Diagram
<code>// Create workflow engine
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// Deploy BPMN file
RepositoryService repositoryService = engine.getRepositoryService();
repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();</code>The deployment code runs in the backend; no additional Java code is needed after the diagram is uploaded.
Start a Process Instance
<code>Map<String, Object> variableMap = new HashMap<>();
variableMap.put("applyUser", "zhang3");
variableMap.put("supervisor", "li4");
variableMap.put("upperSupervisor", "wang5");
ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply_processor_1", variableMap);
</code>This creates a new instance with the applicant and supervisors set as process variables.
Task Handling
The applicant submits a leave request, which creates a task for the first‑level supervisor:
<code>Task firstTask = taskService.createTaskQuery().taskAssignee("zhang3").singleResult();
taskService.complete(firstTask.getId(), Maps.newHashMap("day", 4));
</code>After completion, the engine evaluates the exclusive gateway condition
#{day>3}to decide whether to route to the second‑level supervisor.
Second‑Level Approval
<code>Task secondTask = taskService.createTaskQuery().taskAssignee("li4").singleResult();
taskService.setVariable(secondTask.getId(), "result1", true);
taskService.complete(secondTask.getId());
</code>If the leave exceeds three days, a second task is created for the upper supervisor:
<code>Task thirdTask = taskService.createTaskQuery().taskAssignee("wang5").singleResult();
if (thirdTask != null) {
taskService.setVariable(thirdTask.getId(), "result2", true);
taskService.complete(thirdTask.getId());
}
</code>The engine then proceeds to the final approval or rejection endpoints based on the variables
${result1}and
${result2}.
Viewing Execution History
<code>List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instance.getId()).list();
for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {
log.warn("activityName:{}, activityType:{}, assignee:{}, taskId:{}",
historicActivityInstance.getActivityName(),
historicActivityInstance.getActivityType(),
historicActivityInstance.getAssignee(),
historicActivityInstance.getTaskId());
}
</code>This prints a complete audit trail of every node the process passed through.
Activiti Project Configuration
Dependency
<code><dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>5.23.0</version>
</dependency>
</code>DataSource (H2 for learning)
<code><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;"/>
</bean>
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="databaseSchemaUpdate" value="true"/>
</bean>
<context:component-scan base-package="com.muppet.activiti"/>
</code>H2 Dependency
<code><dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</code>Database Tables Used by Activiti
ACT_RE_* – process definitions and static resources
ACT_RU_* – runtime data such as tasks, process instances, variables
ACT_HI_* – historic data for audit and reporting
ACT_ID_* – user, group, and permission information
Other tables – event logs, jobs, timers for asynchronous handling
Core API Overview
ProcessEngine: entry point to obtain other services
RepositoryService: deploy and manage process definitions
RuntimeService: start, suspend, or delete process instances
TaskService: query, claim, and complete tasks
HistoryService: query completed tasks and historic process data
What Is BPMN?
BPMN (Business Process Modeling Notation) is a standard visual language for describing business processes, enabling clear communication across departments.
Activiti implements BPMN 2.0 and provides a community‑driven, Apache‑licensed engine.
Sample BPMN XML (Leave Process)
<code><definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" ...>
<process id="apply_processor_1" isExecutable="true">
<startEvent id="_4" name="开始">
<extensionElements>
<activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
</extensionElements>
</startEvent>
<userTask id="_5" name="请假申请" activiti:assignee="#{applyUser}"/>
<userTask id="_6" name="主管审批" activiti:assignee="${supervisor}">
<extensionElements>
<activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
</extensionElements>
</userTask>
<exclusiveGateway id="_7" name="一级审批结果"/>
<endEvent id="_8" name="审批不通过"/>
... (remaining BPMN elements omitted for brevity) ...
</process>
</definitions>
</code>Comparing Similar Engines
Camunda and Flowable are both derived from Activiti and share a similar usage flow.
Typical 5‑Step Workflow
Define BPMN diagram with a modeling tool.
Deploy the diagram to the workflow engine.
Start a new process instance.
Execute tasks; the engine creates tasks and assigns them.
Listen to events and monitor execution via provided APIs.
Further Learning Topics
Event types and listeners.
Different task types: user, service, script.
Form management.
Gateways (parallel, exclusive, etc.).
Performance, scaling, ID generation, sharding.
Conclusion
Activiti is ideal for multi‑user, multi‑step business processes.
Development consists of diagram design, deployment, and instance management.
Adding new processes rarely requires extra Java code—only front‑end forms and simple back‑end endpoints.
For a complete microservice project built with Spring Cloud, Kubernetes, and Activiti, see the mall‑swarm repository and its video tutorials.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.