Backend Development 10 min read

Applying the Pipeline Pattern to Decouple Complex Business Logic in Java Backend Systems

The article explains how the Pipeline pattern, a variant of the Chain of Responsibility, can be used to split and decouple complex business scenarios in Java backend applications such as Spring, Tomcat, and Netty, providing code examples, structural diagrams, and practical usage tips.

Architect
Architect
Architect
Applying the Pipeline Pattern to Decouple Complex Business Logic in Java Backend Systems

In domains like e‑commerce, insurance and catering, business logic often becomes tangled because many factors (user identity, membership level, product attributes, location, etc.) lead to numerous branching paths that grow and become hard to maintain; the article proposes using a Pipeline pattern to split and decouple such complexity.

The Pipeline pattern is presented as a specialized form of the Chain of Responsibility. The author first references Spring's core HTTP handling where DispatcherServlet obtains a HandlerExecutionChain that wraps the actual handler together with a series of HandlerInterceptor implementations.

Each HandlerExecutionChain contains three interceptor methods— preHandle() , postHandle() and afterCompletion() —which correspond to three applyXXX methods that are invoked sequentially by iterating over an internal array.

In the Spring AOP module, the class ReflectiveMethodInvocation demonstrates the same idea using recursion; the full source of its proceed() method is shown below:

public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    // 执行完所有增强后执行切点方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // 获取下一个要执行的拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        // 动态匹配
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class
targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        } else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            // 不匹配则不执行拦截器
            return proceed();
        }
    } else {
        // 普通拦截器,直接调用拦截器,比如 ExposeInvocationInterceptor,AspectJAfterAdvice
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // 传入this,形成调用链条,如MethodBeforeAdviceInterceptor,会先执行增强方法,再执行后面逻辑
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

The author notes that the index‑based loop cannot terminate early, while the recursive approach couples the chain tightly and is hard to debug; both are unsuitable for business orchestration.

The proposed Pipeline is a linked‑list structure with head and tail nodes, a shared OrderContext , start/end methods and node‑manipulation utilities. A simplified implementation is provided:

public class DefaultOrderPipeLine implements OrderPipeLine {
    public OrderContext context;
    public OrderHandlerNode head;
    public OrderHandlerNode tail;

    public DefaultOrderPipeLine(OrderContext context) {
        this.context = context;
        head = new OrderHandlerNode();
        tail = head;
    }

    @Override
    public void addFirst(OrderHandler... handlers) {
        OrderHandlerNode handlerNode;
        for (OrderHandler handler : handlers) {
            OrderHandlerNode preNode = head.getNextNode();
            handlerNode = new OrderHandlerNode(handler);
            handlerNode.setNextNode(preNode);
            head.setNextNode(handlerNode);
        }
    }
}

Each node holds a reference to the next node and a concrete OrderHandler :

public class OrderHandlerNode {
    private OrderHandler handler;
    private OrderHandlerNode nextNode;

    public OrderHandlerNode(OrderHandler handler) {
        this.handler = handler;
    }

    public void execute(OrderContext) {
        // execution logic
    }
}

The shared OrderContext carries business data such as skuId , goodsName and amount :

public class OrderContext {
    private String skuId;
    private String goodsName;
    private BigDecimal amount;
}

When the pipeline’s start method is invoked, the context flows through each node like a boat moving downstream; multiple pipelines can be created for different stages (pre‑validation, post‑creation, etc.) and combined as needed.

Tomcat also employs a similar pipeline concept: a four‑layer chain of ValveBase objects where each layer calls the next. Netty’s pipeline differs by separating inbound and outbound events; each DefaultChannelHandlerContext stores a handler and an EventExecutor , and the pipeline is a doubly‑linked list that processes events from head to tail or tail to head depending on the operation.

The author suggests pre‑initialising pipelines for performance‑critical scenarios, using a bidirectional list when both entry and exit processing are required, and attaching dedicated executors to nodes for thread‑pool isolation.

A complete demo implementation is available on GitHub at https://github.com/jekran/order_pipeline ; readers are encouraged to clone, test, and give the article a like if they find it useful.

backendJavaSpringresponsibility chainPipeline
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login 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.