Backend Development 23 min read

Design and Implementation of a Lightweight Java Workflow Engine

This article explains why a custom lightweight workflow engine was built, outlines its micro‑kernel architecture, demonstrates incremental development from a Hello‑World example to a simple approval process with branching, and discusses future enhancements such as exception handling, persistence, and dynamic graph modification.

JD Tech Talk
JD Tech Talk
JD Tech Talk
Design and Implementation of a Lightweight Java Workflow Engine

1. What is a workflow engine? A workflow engine is a set of code that drives the execution of a workflow. The article assumes readers are familiar with the concept of workflows and their use cases.

2. Why reinvent the wheel? Existing open‑source engines (Activiti, Flowable, Camunda) do not meet specific business requirements, have high learning curves, and introduce maintenance risks. Building a custom engine allows full control, minimal dependencies (only JDK 8), and strategic ownership of core technology.

3. How to build the engine Three development approaches are suggested: fixed‑scope delivery, iterative development, and divide‑and‑conquer. The design focuses on being lightweight, micro‑kernel based, and only implements a subset of BPMN needed for the target scenarios.

4. Hello ProcessEngine (first iteration) The goal is to print "Hello ProcessEngine" using an XML definition of the process. The XML representation is:

<definitions>
    <process id="process_1" name="hello">
        <startEvent id="startEvent_1">
            <outgoing>flow_1</outgoing>
        </startEvent>
        <sequenceFlow id="flow_1" sourceRef="startEvent_1" targetRef="printHello_1" />
        <printHello id="printHello_1" name="hello">
            <incoming>flow_1</incoming>
            <outgoing>flow_2</outgoing>
        </printHello>
        <sequenceFlow id="flow_2" sourceRef="printHello_1" targetRef="printProcessEngine_1" />
        <printProcessEngine id="printProcessEngine_1" name="processEngine">
            <incoming>flow_2</incoming>
            <outgoing>flow_3</outgoing>
        </printProcessEngine>
        <sequenceFlow id="flow_3" sourceRef="printProcessEngine_1" targetRef="endEvent_1"/>
        <endEvent id="endEvent_1">
            <incoming>flow_3</incoming>
        </endEvent>
    </process>
</definitions>

The corresponding Java model classes are:

public class PeProcess {
public String id;
public PeNode start;
public PeProcess(String id, PeNode start) { this.id = id; this.start = start; }
}
public class PeEdge {
private String id;
public PeNode from;
public PeNode to;
public PeEdge(String id) { this.id = id; }
}
public class PeNode {
private String id;
public String type;
public PeEdge in;
public PeEdge out;
public PeNode(String id) { this.id = id; }
}

The engine logic iterates from the start node, follows outgoing edges, executes node‑specific logic, and stops at the end event.

5. Simple approval (second iteration) A linear approval flow is modeled with XML and three operator implementations (apply, approve, notify). The XML adds nodes for approvalApply , approval , and notify . The engine registers each operator via registNodeProcessor and runs the process.

6. General approval with branching (third iteration) To support decision logic, a simpleGateway node is introduced. Its XML includes an expr element and a trueOutGoing edge. The gateway operator evaluates the expression using a JavaScript engine:

public class OperatorOfSimpleGateway implements IOperator {
@Override public String getType() { return "simpleGateway"; }
@Override public void doTask(ProcessEngine engine, PeNode node, PeContext ctx) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine js = mgr.getEngineByName("js");
js.put("approvalResult", ctx.getValue("approvalResult"));
String expr = XmlUtil.childTextByName(node.xmlNode, "expr");
String trueEdge = XmlUtil.childTextByName(node.xmlNode, "trueOutGoing");
PeEdge out = (Boolean) js.eval(expr) ? node.outWithID(trueEdge) : node.outWithOutID(trueEdge);
engine.nodeFinished(out);
}
}

The updated PeNode now holds lists of incoming and outgoing edges, enabling multiple branches.

7. Summary and outlook The engine now has a stable core, extensible operator layer, context for global variables, and supports sequential, branching, and loop‑like structures. Future work includes exception handling, persistence, DAG validation, dynamic graph updates, concurrency control, and richer rule engines.

JavamicrokernelBPMNsoftware designprocess-engineworkflow engineApproval Workflow
JD Tech Talk
Written by

JD Tech Talk

Official JD Tech public account delivering best practices and technology innovation.

0 followers
Reader feedback

How this landed with the community

login 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.