Escape the if‑else Nightmare: Why Workflow Orchestration Is a Game‑Changer
The article explains how excessive if‑else branching in multi‑business systems leads to maintenance chaos, and demonstrates how a workflow engine combined with plugin extensions can isolate code, simplify extensions, and improve reliability through configurable flow chains and node execution.
When a system must support many business lines, developers often use nested if‑else statements, which makes the code hard to read, maintain, and extend, and increases the risk of bugs affecting unrelated services.
Typical branching structure:
if (biz == BizA || biz == BizB) {
// common logic
if (biz == BizA) {
// BizA‑specific handling
}
if (biz == BizB) {
// BizB‑specific handling
}
}Adding new business logic requires additional branches, leading to tangled code and regression risk.
Two techniques are applied in the MemberClub project:
Use a workflow engine that lets each business configure its own execution chain.
Use a plugin‑extension engine so that business‑specific logic resides in separate plugins.
Configuring flow execution chains
DemoMemberPurchaseExtension defines three ways to configure the flow chain for different membership purchase processes. (Image shows configuration.)
Defining flow nodes
Each node implements four methods: process, success, rollback, and callback. (Image shows node definition.)
Executing a flow
Execution requires a context object and a single call to FlowChain.execute:
FlowChain.execute(chain, context);During execution, nodes are linked like a responsibility chain. The engine calls each node’s process method in order; if an exception occurs, it triggers rollback on already‑executed nodes. When all process calls succeed, the engine invokes success on each node in reverse order, followed by callback for final cleanup.
Flow engine execution principle
The core of the engine is the FlowChain.execute method:
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 further execution", 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:
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.
SpringMeng
Focused on software development, sharing source code and tutorials for various systems.
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.
