Operations 25 min read

Choosing the Right Workflow Engine: Activiti, Flowable, Camunda, jBPM & JDEasyFlow

This article explains what workflow engines are, compares five popular options—Activiti, Flowable, Camunda, jBPM and JDEasyFlow—detailing their core principles, architecture, code examples, and suitable scenarios, and provides a comprehensive table and decision flowchart to help developers select the most appropriate engine for their projects.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Choosing the Right Workflow Engine: Activiti, Flowable, Camunda, jBPM & JDEasyFlow

Workflow engines are the core support systems for business process automation, coordinating the sequence of business nodes much like traffic signals manage vehicle flow.

What Is a Workflow Engine?

A workflow engine abstracts complex business logic into discrete tasks and controls the flow between them, allowing visual process definition and decoupling business logic from flow control.

Typical benefits include reduced complexity, improved maintainability, enhanced visualization, and guaranteed consistency through state machines.

Activiti: Enterprise‑Grade Standard Workflow Engine

Core Principles

Activiti is based on the BPMN 2.0 standard and uses a state‑machine plus command‑pattern design. It models processes as a series of activity nodes with tokens moving between them.

Architecture Features

Full BPMN 2.0 element support

Command pattern encapsulates all operations

State‑machine manages process instance state

Transactional process execution

Open‑Source Repository

GitHub: https://github.com/Activiti/Activiti

Example: Leave Approval Process

Dependency for Spring Boot:

<dependency>
  <groupId>org.activiti</groupId>
  <artifactId>activiti-spring-boot-starter</artifactId>
  <version>7.1.0.M6</version>
</dependency>

BPMN definition (leave‑approval.bpmn20.xml):

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
  <process id="leaveApproval" name="请假审批流程" isExecutable="true">
    <!-- Start Event -->
    <startEvent id="startEvent"/>
    <!-- User submits application -->
    <userTask id="submitLeave" name="提交请假申请" activiti:assignee="#{employee}"/>
    <!-- Supervisor approval -->
    <userTask id="leaderApproval" name="直接主管审批" activiti:assignee="#{leader}"/>
    <!-- Decision gateway -->
    <exclusiveGateway id="decisionGateway"/>
    <!-- Approved flow -->
    <sequenceFlow id="flowApproved" sourceRef="decisionGateway" targetRef="hrRecord">
      <conditionExpression xsi:type="tFormalExpression">${approved == true}</conditionExpression>
    </sequenceFlow>
    <!-- Rejected flow -->
    <sequenceFlow id="flowRejected" sourceRef="decisionGateway" targetRef="rejectEnd">
      <conditionExpression xsi:type="tFormalExpression">${approved == false}</conditionExpression>
    </sequenceFlow>
    <!-- HR record -->
    <userTask id="hrRecord" name="HR备案" activiti:assignee="#{hr}"/>
    <!-- End Event -->
    <endEvent id="endEvent"/>
  </process>
</definitions>

Java service to deploy and start the process:

@Service
@Transactional
public class LeaveProcessService {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;

    // Deploy process definition
    public void deployProcess() {
        repositoryService.createDeployment()
            .addClasspathResource("processes/leave-approval.bpmn20.xml")
            .name("请假审批流程")
            .deploy();
    }

    // Start leave process
    public void startLeaveProcess(String employee, String leader, int leaveDays) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("employee", employee);
        variables.put("leader", leader);
        variables.put("hr", "hr_department");
        variables.put("leaveDays", leaveDays);
        variables.put("approved", null);
        ProcessInstance pi = runtimeService.startProcessInstanceByKey("leaveApproval", variables);
        System.out.println("流程启动成功,实例ID:" + pi.getId());
    }

    // Query pending tasks
    public List<Task> getPendingTasks(String assignee) {
        return taskService.createTaskQuery()
            .taskAssignee(assignee)
            .orderByTaskCreateTime().desc()
            .list();
    }

    // Complete task with approval result
    public void completeTask(String taskId, boolean approved, String comment) {
        Map<String, Object> vars = new HashMap<>();
        vars.put("approved", approved);
        if (comment != null && !comment.trim().isEmpty()) {
            taskService.addComment(taskId, null, comment);
        }
        taskService.complete(taskId, vars);
        System.out.println("任务完成,审批结果:" + (approved ? "通过" : "驳回"));
    }
}

Flowable: Performance‑Optimized Fork of Activiti

Core Principles

Flowable adds an asynchronous executor and execution‑tree optimization, using finer‑grained lock management and batch processing to improve high‑concurrency performance.

Architecture Improvements

Enhanced asynchronous executor with batch operations

Optimized execution tree reducing DB access

Better cache management and lazy loading

Native Spring Boot starter support

Open‑Source Repository

GitHub: https://github.com/flowable/flowable-engine

Example: Order Processing with Service Tasks

@Service
public class OrderProcessService {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private RepositoryService repositoryService;

    public void deployOrderProcess() {
        repositoryService.createDeployment()
            .addClasspathResource("processes/order-process.bpmn20.xml")
            .addClasspathResource("processes/order-discount.dmn") // DMN decision table
            .deploy();
    }

    public void startOrderProcess(Order order) {
        Map<String, Object> vars = new HashMap<>();
        vars.put("order", order);
        vars.put("customerService", "cs_team");
        vars.put("warehouse", "wh_team");
        ProcessInstance pi = runtimeService.startProcessInstanceByKey("orderProcess", order.getOrderNo(), vars);
        logger.info("订单流程启动: {}", pi.getId());
    }
}

@Component
public class InventoryCheckDelegate implements JavaDelegate {
    @Autowired
    private InventoryService inventoryService;
    @Override
    public void execute(DelegateExecution execution) {
        Order order = (Order) execution.getVariable("order");
        boolean inStock = inventoryService.checkInventory(order.getProductId(), order.getQuantity());
        execution.setVariable("inStock", inStock);
        if (!inStock) {
            execution.setVariable("needBackorder", true);
            logger.warn("产品{}库存不足,需要备货", order.getProductId());
        }
    }
}

@Component
public class PaymentProcessDelegate implements JavaDelegate {
    @Autowired
    private PaymentService paymentService;
    @Override
    public void execute(DelegateExecution execution) {
        Order order = (Order) execution.getVariable("order");
        try {
            PaymentResult result = paymentService.processPayment(order);
            execution.setVariable("paymentSuccess", result.isSuccess());
            execution.setVariable("paymentId", result.getPaymentId());
        } catch (Exception e) {
            execution.setVariable("paymentSuccess", false);
            execution.setVariable("paymentError", e.getMessage());
            throw new RuntimeException("支付处理失败", e);
        }
    }
}

Camunda: Preferred for Microservice Architectures

Core Principles

Camunda uses an external‑task model and optimistic‑lock concurrency control, decoupling the workflow engine from business services and enabling language‑agnostic workers via task queues.

Architecture Features

External task mode supports multi‑language clients

Full monitoring tools (Cockpit, Tasklist)

Optimistic lock for high concurrency

Native microservice integration

Open‑Source Repository

GitHub: https://github.com/camunda/camunda-bpm-platform

Example: Loan Approval with External Tasks

@Service
@Transactional
public class LoanProcessService {
    @Autowired
    private RuntimeService runtimeService;
    public void startLoanProcess(LoanApplication application) {
        Map<String, Object> vars = new HashMap<>();
        vars.put("application", application);
        vars.put("creditCheckRequired", application.getAmount() > 100000);
        vars.put("autoApprovalLimit", 50000.0);
        ProcessInstance pi = runtimeService.startProcessInstanceByKey("loanApprovalProcess", application.getApplicationId(), vars);
        logger.info("贷款流程启动: {}", pi.getId());
    }
}

@Component
public class CreditCheckWorker {
    @Autowired
    private ExternalTaskService externalTaskService;
    @Autowired
    private CreditService creditService;
    @PostConstruct
    public void startCreditCheckWorker() {
        new Thread(() -> {
            while (true) {
                try {
                    List<LockedExternalTask> tasks = externalTaskService.fetchAndLock(10, "credit-check-worker")
                        .topic("credit-check", 60000L)
                        .execute();
                    for (LockedExternalTask task : tasks) {
                        processCreditCheck(task);
                    }
                    Thread.sleep(5000);
                } catch (Exception e) {
                    logger.error("征信检查工作者异常", e);
                }
            }
        }).start();
    }
    private void processCreditCheck(LockedExternalTask task) {
        try {
            LoanApplication app = (LoanApplication) task.getVariable("application");
            CreditReport report = creditService.getCreditReport(app.getApplicantName(), app.getApplicantId());
            Map<String, Object> vars = new HashMap<>();
            vars.put("creditReport", report);
            vars.put("creditScore", report.getScore());
            vars.put("creditCheckPassed", report.getScore() > 600);
            externalTaskService.complete(task.getId(), "credit-check-worker", vars);
            logger.info("征信检查完成: {}", app.getApplicationId());
        } catch (Exception e) {
            externalTaskService.handleFailure(task.getId(), "credit-check-worker", e.getMessage(), 3, 30000L);
        }
    }
}

jBPM: Best Choice for Rule Integration

Core Principles

jBPM uses a Knowledge Session model that unifies the process engine and Drools rule engine, allowing seamless collaboration between workflow and business rules.

Architecture Features

Deep Drools integration

Knowledge‑session unified execution environment

Supports BPMN 2.0 and case management

Full lifecycle management

Open‑Source Repository

GitHub: https://github.com/kiegroup/jbpm

Example: Insurance Claim with Rule Tasks

@Service
public class ClaimProcessService {
    @Autowired
    private RuntimeEngine runtimeEngine;
    @Autowired
    private KieContainer kieContainer;
    public void startClaimProcess(InsuranceClaim claim) {
        KieSession ks = kieContainer.newKieSession();
        try {
            ks.insert(claim);
            ks.insert(new Date());
            ProcessInstance pi = runtimeEngine.getKieSession()
                .startProcess("claimApprovalProcess", createProcessVariables(claim));
            logger.info("理赔流程启动: {}", pi.getId());
        } finally {
            ks.dispose();
        }
    }
    private Map<String, Object> createProcessVariables(InsuranceClaim claim) {
        Map<String, Object> vars = new HashMap<>();
        vars.put("claim", claim);
        vars.put("claimAmount", claim.getClaimAmount());
        vars.put("claimType", claim.getClaimType());
        vars.put("autoApprovalLimit", 5000.0);
        vars.put("requiresManagerApproval", claim.getClaimAmount() > 10000);
        return vars;
    }
}

// DRL rule example
rule "High Amount Fraud Detection"
    when
        $claim : InsuranceClaim(claimAmount > 50000)
        Date()
    then
        $claim.setFraudulent(true);
        modify($claim);
        System.out.println("检测到大额理赔,标记为可疑欺诈: " + $claim.getClaimId());
end

JDEasyFlow: Lightweight Flow Orchestration Star

Core Principles

JDEasyFlow defines processes using JSON and a state‑machine engine, following a "convention over configuration" approach for simple orchestration without a database.

Architecture Features

JSON‑based declarative definitions

In‑memory execution, no DB dependency

Supports a subset of BPMN elements

Very low learning curve

Open‑Source Repository

GitHub: https://github.com/JDEasyFlow/jd-easyflow

Example: Simple Order State Flow

{
  "id": "order_flow",
  "name": "订单状态流程",
  "nodes": [
    {"id": "created", "name": "订单创建", "start": true, "post": {"to": "paid"}},
    {"id": "paid", "name": "已支付", "action": {"createExp": "new com.example.order.PaymentAction()"}, "post": {"to": "shipped"}},
    {"id": "shipped", "name": "已发货", "action": {"createExp": "new com.example.order.ShipmentAction()"}, "post": {"to": "received"}},
    {"id": "received", "name": "已收货", "action": {"createExp": "new com.example.order.ReceiveAction()"}, "post": {"to": "completed"}},
    {"id": "completed", "name": "已完成"},
    {"id": "cancelled", "name": "已取消"}
  ]
}
@Component
public class PaymentAction implements FlowNodeAction {
    @Autowired
    private PaymentService paymentService;
    @Override
    public Object execute(FlowRequest request, FlowContext context) {
        String orderId = (String) request.getParam("orderId");
        PaymentResult result = paymentService.confirmPayment(orderId);
        Map<String, Object> data = new HashMap<>();
        data.put("paymentId", result.getPaymentId());
        data.put("paymentTime", result.getPaymentTime());
        data.put("success", result.isSuccess());
        logger.info("订单{}支付处理完成", orderId);
        return data;
    }
}

Comprehensive Comparison of the Five Workflow Engines

Feature Dimension          | Activiti | Flowable | Camunda | jBPM | JDEasyFlow
--------------------------|----------|----------|---------|------|------------
Learning Curve            | Medium   | Medium   | Steep   | Steep| Simple
Performance               | Good     | Excellent| Excellent| Good | Outstanding
Feature Completeness      | High     | High     | Very High| High | Medium
Monitoring & Ops           | Basic    | Good     | Excellent| Good | Simple
Community Ecosystem        | Active   | Active   | Active  | Active| Limited
Cloud‑Native Support       | Basic    | Good     | Excellent| Good | Good
Rule Integration           | Basic    | Basic    | Good    | Excellent| None
Typical Use Cases          | Traditional enterprise apps | High‑throughput business | Microservice architectures | Rule‑intensive scenarios | Lightweight service orchestration

Decision flowchart (visual guide for engine selection):

Decision Flowchart
Decision Flowchart

Conclusion

Traditional enterprise applications – choose Activiti for mature ecosystem and rich documentation.

High‑performance, high‑concurrency scenarios – choose Flowable for its performance optimizations.

Microservice architectures – choose Camunda for its external‑task model and robust monitoring.

Rule‑intensive business – choose jBPM for deep Drools integration.

Lightweight service orchestration – choose JDEasyFlow for simplicity and low learning cost.

Remember, there is no universally best workflow engine; the optimal choice depends on your team’s tech stack, business requirements, performance needs, and operational capabilities.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BPMNEngine
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.