Why Workflow Engines Are Essential for Scalable Backend Systems
The article explains how over‑design can be avoided by using workflow orchestration and plugin extensions to isolate business logic, reduce if‑else sprawl, and improve extensibility, illustrated with the open‑source MemberClub project and its Java flow‑engine implementation.
A good programmer should keep solutions simple and avoid over‑design. I once dismissed workflow orchestration as unnecessary, but working in a middle‑platform team showed me its vital role for code isolation and extensibility.
In a business middle platform many different business lines need to be integrated. Adding new business code must not affect existing functionality, yet without workflow orchestration the code quickly fills with numerous if‑else branches, making maintenance painful and error‑prone.
Key problems:
Code isolation and business extension points become tangled.
Every new business adds more conditional branches, leading to a "mountain of shit".
Solutions:
Use a workflow engine to configure separate execution chains for each business.
Use a plugin‑extension engine to implement business‑specific differences.
The open‑source project MemberClub demonstrates these solutions. It provides a paid‑membership transaction solution and serves as a learning example for middle‑platform architecture.
Configure Flow Execution Chain
Different membership products have different order‑processing flows. The class DemoMemberPurchaseExtension defines three flow‑chain configurations.
Define Flow Nodes
Each flow node implements the methods process, success, rollback and callback.
Flow Execution
To execute a flow, provide a context object and call FlowChain.execute.
Flow Engine Execution Principle
The following code shows the core 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("Current flow:{} sent Skip request, stop 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, ignore 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, ignore 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, ignore name:{}", node.getClass().getSimpleName(), e);
}
}
if (exception != null) {
throw exception;
}
}The full source code is available in the MemberClub repository on Gitee and GitHub.
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
