Escape the If‑Else Nightmare with Powerful Workflow Orchestration
The article explains how excessive if‑else branching in multi‑business Java systems harms maintainability and reliability, and demonstrates how a flow engine combined with plugin extensions can isolate code, configure per‑business execution chains, and safely handle success, rollback, and callbacks.
When many business lines share a single codebase, developers tend to accumulate extensive if‑else branches to handle divergent logic, which reduces code isolation, hampers testing, and increases the risk of regressions.
Two mechanisms are introduced to address these problems:
Use a flow engine to configure separate execution chains for each business scenario.
Use a plugin‑extension engine so that each business can provide its own divergent implementation.
The MemberClub project applies both mechanisms to achieve isolation and extensibility.
Configure Flow Execution Chains
The demo class DemoMemberPurchaseExtension implements a purchase extension point and demonstrates three ways to configure a flow chain. Each configuration results in a distinct chain of FlowNode objects.
Define Flow Nodes
Every node implements four lifecycle methods: process – core business logic. success – invoked after all process calls succeed, in reverse order. rollback – invoked when a process throws an exception, also in reverse order. callback – always executed after process, regardless of success or failure.
Execute the Flow
Execution requires a context object and a call to FlowChain.execute. The engine iterates over the nodes, invoking process sequentially. If a node throws an exception, the loop stops, the exception is recorded, and the engine proceeds to the rollback phase. If no exception occurs, the engine runs the success phase. Finally, the callback phase runs for all visited nodes, and the original exception (if any) is re‑thrown.
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;
}
}Project Source Code
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.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
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.
