Mastering Flow Engines: Simplify Business Logic with Process Chains
This article explains why over‑designed code hampers maintainability, introduces flow engines and plugin extension engines as solutions for isolating business logic, and provides practical guidance on configuring flow chains, defining nodes, and executing processes with Java examples.
As a competent programmer, you should avoid over‑design and keep systems simple; using straightforward solutions for simple tasks leads to cleaner code.
Initially I dismissed workflow orchestration as unnecessary, but working in a middle‑platform team revealed its critical role in maintaining code isolation and extensibility across many business lines.
Without a flow engine, adding new business features quickly results in tangled if‑else statements, making testing and maintenance a nightmare.
Use a flow engine to configure distinct execution chains for each business scenario.
Employ a plugin extension engine to implement business‑specific variations.
MemberClub heavily relies on these engines to solve isolation and extensibility challenges.
Configure Flow Execution Chain
Different membership products require distinct purchase processes; the DemoMemberPurchaseExtension demonstrates three configuration methods for execution chains.
Define Flow Nodes
Each node implements process, success, rollback, and callback methods.
Flow Execution
Provide a context object and invoke FlowChain.execute to run the chain.
During execution, nodes are linked like a responsibility chain: each process runs in order, failures trigger rollback, and successful runs invoke success in reverse order, followed by callback.
Flow Engine Execution Principle
The following code shows the internal logic of FlowChain.execute.
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("当前流程:{} 发出 Skip请求,后续流程不再执行", 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执行异常,忽略 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 执行异常,忽略 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执行异常,忽略 name:{}", node.getClass().getSimpleName(), e);
}
}
if (exception != null) {
throw exception;
}
}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.
