Spring Boot + 执行管道:让业务流程清晰可控

不知道你有没有遇到过这种情况:一个订单创建的接口,刚上线的时候只有几十行代码,逻辑也很简单。但随着业务迭代,三个月后变成了这样:

scss 复制代码
public void createOrder(OrderRequest request) {
    // 参数校验
    if (request.getUserId() == null) {
        throw new IllegalArgumentException("用户ID不能为空");
    }
    // ... 省略 20 行校验代码

    // 权限校验
    if (!checkPermission(request.getUserId())) {
        throw new ForbiddenException("无权限");
    }

    // 业务校验
    if (getOrderCount(request.getUserId()) >= 10) {
        throw new BusinessException("订单数量超限");
    }
    // ... 省略 15 行业务校验

    // 创建订单
    Order order = buildOrder(request);
    orderMapper.insert(order);

    // 写操作日志
    try {
        operateLogService.log(order.getId(), "CREATE");
    } catch (Exception e) {
        // 日志失败不影响主流程
        log.error("写日志失败", e);
    }

    // 发送通知
    try {
        notificationService.send(order);
    } catch (Exception e) {
        log.error("发送通知失败", e);
    }

    // 异步风控检查
    asyncRiskCheckService.check(order);
}

几百行代码,iftry-catch 到处都是。更可怕的是:

  • • 日志写失败要不要抛异常?
  • • 通知发送失败要不要回滚订单?
  • • 新增一个"优惠券核销"逻辑加在哪里?
  • • 测试环境想关闭风控检查怎么搞?

问题出在哪?

仔细看看上面的代码,其实它想做的事情可以拆成这么几个步骤:

markdown 复制代码
1. 校验参数
2. 校验权限
3. 校验业务规则
4. 创建订单(核心)
5. 记录日志
6. 发送通知
7. 风控检查

这些步骤有几个特点:

有的必须成功 :比如参数校验、订单创建
有的可以失败 :比如日志记录、通知发送
有的同步执行 :比如各种校验
有的异步执行 :比如风控检查
有的可能要关闭:比如测试环境不需要风控

但现在的写法,所有这些逻辑都混在一起,顺序、失败策略、是否可选 ,全靠代码里的 iftry-catch 来控制,能不乱吗?

有没有更好的写法?

如果把这些步骤抽象成一个个"节点",然后用一个"管道"把它们串起来,会怎样?

csharp 复制代码
public interface PipelineNode<T> {
    void execute(PipelineContext<T> context);
}

每个节点只做一件事,不关心前后是谁,不关心自己是第几个执行。

然后定义一个管道来执行这些节点:

swift 复制代码
public class ExecutionPipeline<T> {

    private final List<PipelineNode<T>> nodes;

    public void execute(T data) {
        PipelineContext<T> ctx = new PipelineContext<>(data);
        for (PipelineNode<T> node : nodes) {
            if (ctx.isInterrupted()) {
                break;
            }
            node.execute(ctx);
        }
    }
}

现在再看看订单创建怎么写:

csharp 复制代码
Pipeline pipeline = Pipeline.builder()
    .add(new ParamValidateNode())       // 参数校验
    .add(new PermissionCheckNode())     // 权限校验
    .add(new BusinessValidateNode())    // 业务校验
    .add(new CreateOrderNode())         // 创建订单
    .add(new OperateLogNode())          // 写日志
    .add(new NotificationNode())        // 发通知
    .add(new AsyncRiskCheckNode())      // 风控检查
    .build();

pipeline.execute(orderRequest);

主流程只剩下 10 行代码,每个节点的逻辑独立封装。

这样做有什么好处?

1. 节点可插拔

想加一个"优惠券核销"逻辑?加个节点就行了:

csharp 复制代码
.add(new CouponDeductNode())

想关闭某个功能?去掉对应节点,或者通过配置控制:

scss 复制代码
.add(env.isProd() ? new RiskCheckNode() : new SkipNode())

2. 失败策略清晰

PipelineContext 里定义好失败策略:

arduino 复制代码
public enum FailureStrategy {
    CONTINUE,    // 失败后继续执行
    STOP,        // 失败后中断管道
    ROLLBACK     // 失败后回滚(需要配合事务)
}

日志节点的失败策略是 CONTINUE,订单创建节点的失败策略是 STOP,一目了然。

3. 节点可复用

参数校验节点、权限校验节点、日志节点,都可以在其他业务里复用。

4. 执行顺序可控

节点的执行顺序由管道定义,不用在每个节点里写代码控制。

执行管道:一种工程化方案

上面介绍的方案既执行管道(Execution Pipeline)

它不是某个框架,而是一种工程设计模式:把一次业务执行拆成多个可插拔、有顺序、可控制的执行阶段,用统一机制串起来。

它跟 AOP、责任链有什么区别?

你可能想到了 AOP、责任链、工作流引擎这些概念,它们确实有相似之处:

方案 适用场景 局限性
AOP 通用的横切逻辑(日志、事务) 顺序不直观,难以做流程控制
Filter/Interceptor HTTP 请求级别的处理 只能用于请求处理
@Async 异步执行 只解决并发,不解决流程编排
责任链 动态组合处理逻辑 缺少工程化封装
工作流引擎 复杂的业务流程 太重,单机应用用不上

执行管道可以说是工程化的责任链模式,专门用于解决单机应用内的业务流程编排问题。

什么时候用?

不是所有场景都需要执行管道。如果你的代码:

  • • 步骤固定、逻辑简单,直接写就行
  • • 流程复杂但不会变化,也没必要过度设计

但当出现这些信号时,可以考虑:

  • • 一个方法过长,不利于阅读维护
  • • 有多个"可选"或"异步"的执行步骤
  • • 经常需要插入新的执行逻辑
  • • 不同环境需要不同的执行流程

执行管道特别适合单机应用内的复杂业务流程,比如订单创建、用户注册、数据导入等场景。

写在最后

好的代码不是一次写成的,而是在不断重构中进化出来的。

如果你的项目里已经有了那种"巨无霸"方法,不用急着全部推倒重来。先试着把其中一个独立的逻辑抽成节点,然后逐步迁移,代码会越来越清晰。

ruby 复制代码
https://github.com/yuboon/java-examples/tree/master/springboot-pipeline
相关推荐
CaffeinePro20 分钟前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax1 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH1 小时前
Koa和Express的区别
后端
MariaH1 小时前
Koa框架的使用
后端
luckdewei2 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某3 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy3 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom4 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
用户1474853079748 小时前
CodeX使用Skill生成游戏美术和音乐资源,一分钟入门
后端
Melody1238 小时前
用 abort 中断 AI 流式请求,我之前做错了
后端