Mastering Workflow Engines: Simplify Business Logic with Flow Chains in SpringBoot
This article explains why over‑design hurts maintainability, demonstrates how a workflow engine and plugin extension can isolate business logic and enable scalable process chains, and provides step‑by‑step code examples and configuration guidance for SpringBoot projects.
As a good programmer you should keep solutions simple and avoid over‑design; excessive if‑else logic makes code hard to maintain.
When many business lines need slightly different behavior, the code quickly becomes a tangled mess of conditional branches, risking bugs and regression failures.
if (biz == BizA || biz == BizB) {
// common logic
if (biz == BizA) {
// BizA specific handling
}
if (biz == BizB) {
// BizB specific handling
}
}Without isolation, adding a new business line forces you to add more branches, leading to a maintenance nightmare.
Use a workflow engine to configure separate execution chains for each business scenario.
Use a plugin extension engine to implement business‑specific differences.
MemberClub extensively uses a workflow engine and plugin extension engine to solve isolation and extensibility challenges.
Configure Flow Execution Chain
Different membership products require different purchase processes. The DemoMemberPurchaseExtension implements a purchase extension point and defines three ways to configure the execution chain. The diagram below illustrates the configuration.
Define Flow Nodes
A flow node contains process, success, rollback and callback methods.
Flow Execution
During execution you provide a flow context object and invoke FlowChain.execute.
FlowChain.executeThe engine links nodes like a chain of responsibility: each node’s process runs in order; if an exception occurs, the engine rolls back previously executed nodes; if all processes succeed, success methods run in reverse order, followed by callback for every node.
Flow Engine Execution Principle
The following code shows the core logic of FlowChain.execute, handling processing, rollback, success, and callback phases.
public <T> void execute(FlowChain<T> chain, T context) {
Exception exception = null;
int index = -1;
for (FlowNode<T> node : chain.getNodes()) {
try {
node.process(context);
index++;
} catch (Exception e) {
if (e instanceof SkipException) {
CommonLog.warn("Current flow:{} sent Skip request, stop later flows", node.getClass().getSimpleName());
break;
}
exception = e;
break;
}
}
if (exception != null) {
for (int i = index; i >= 0; i--) {
FlowNode<T> node = chain.getNodes().get(i);
try {
node.rollback(context, exception);
} catch (Exception e) {
CommonLog.error("rollback exception ignored name:{}", node.getClass().getSimpleName(), e);
}
}
} else {
for (int i = index; i >= 0; i--) {
FlowNode<T> node = chain.getNodes().get(i);
try {
node.success(context);
} catch (Exception e) {
CommonLog.error("success exception ignored name:{}", node.getClass().getSimpleName(), e);
}
}
}
for (int i = index; i >= 0; i--) {
FlowNode<T> node = chain.getNodes().get(i);
try {
node.callback(context, exception);
} catch (Exception e) {
CommonLog.error("callback exception ignored name:{}", node.getClass().getSimpleName(), e);
}
}
if (exception != null) {
throw exception;
}
}Project Source Code
Source repository: https://gitee.com/juejinwuyang/memberclub
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
