Why Over‑Design Breaks Your Code and How a Flow Engine Restores Simplicity

The article explains how excessive if‑else branching in backend services leads to fragile code, introduces a workflow engine and plugin extension mechanism to isolate business logic, shows concrete configuration and execution steps using the open‑source MemberClub project, and provides the full Java implementation of the FlowChain executor.

Top Architect
Top Architect
Top Architect
Why Over‑Design Breaks Your Code and How a Flow Engine Restores Simplicity

As a senior architect, the author argues that a good programmer should avoid over‑design and keep implementations as simple as possible. In a business‑centered architecture, many different partners need slightly different processing, which often results in massive if‑else blocks that are hard to maintain and test.

Problem: Code entanglement and maintenance risk

When ten or more business lines are integrated, each with its own variations, developers end up writing code like:

if (biz == BizA || biz == BizB) {
    // common logic
    if (biz == BizA) {
        // BizA specific handling
    }
    if (biz == BizB) {
        // BizB specific handling
    }
}

Adding a new business requires inserting more branches, increasing the chance of bugs and making regression testing across all lines unrealistic.

Solution: Workflow engine + plugin extension

Configure a separate flow chain for each business scenario.

Implement business‑specific extensions as plugins that the engine invokes.

The open‑source MemberClub project (hosted on Gitee) demonstrates this approach. It provides a paid‑membership transaction solution and uses the workflow engine to isolate business logic.

Configuring the flow chain

Each membership product defines its own flow chain by implementing an extension point, e.g. DemoMemberPurchaseExtension. The configuration is visualised in the following diagram:

Flow chain configuration diagram
Flow chain configuration diagram

Defining flow nodes

A flow node implements four methods:

process
success
rollback
callback

The node lifecycle is shown below:

Flow node methods diagram
Flow node methods diagram

Flow execution

During execution the engine calls FlowChain.execute with a context object. Nodes are processed sequentially; if a node throws an exception, the engine jumps to the rollback phase. If all process calls succeed, the engine runs the success methods in reverse order, followed by callback for each node.

Execution flow diagram
Execution flow diagram

Core implementation 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 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 execution 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 execution 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 execution exception, ignore name:{}", node.getClass().getSimpleName(), e);
        }
    }
    if (exception != null) {
        throw exception;
    }
}

Where to find the full source

The complete code resides in the MemberClub repository:

https://gitee.com/juejinwuyang/memberclub

Specific file:

https://gitee.com/-/ide/project/juejinwuyang/memberclub/edit/master/-/memberclub.common/src/main/java/com/memberclub/common/flow/FlowChainService.java

The project also integrates many useful components (MyBatis‑Plus, ShardingSphere, Redis, Apollo, Spring Cloud, RabbitMQ, Swagger, Lombok, etc.), making it a valuable reference for building robust business‑centered backends.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

design-patternsBackend Developmentworkflow engine
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.