吸积效应:为什么接口会越来越臃肿?我们从一个接口说起

1 从一个接口说起

1.1 初始接口

假设现在有一个创建订单接口:

xml 复制代码
public OrderCreateResultDTO createOrder(OrderCreateDTO order)
  • 创建订单对象
xml 复制代码
public class OrderCreateDTO {
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnC;
    private String bizColumnD;
}
  • 订单响应对象
xml 复制代码
public class OrderCreateResultDTO {
	private String orderId;
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnC;
    private String bizColumnD;
}

1.2 发生变化

现在业务发生变化,产品经理提出需求:创建订单时不需要C和D字段,新增E和F字段。如果你是开发人员,你会怎么设计这个接口?有一种比较朴素的思路是直接删除C和D字段,新增E和F字段:

  • 创建订单对象
xml 复制代码
public class OrderCreateDTO {
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnE;
    private String bizColumnF;
}
  • 订单响应对象
xml 复制代码
public class OrderCreateResultDTO {
	private String orderId;
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnE;
    private String bizColumnF;
}

1.3 发现问题

我们想一想上述方案是否可行呢?答案是不行,有以下三方面原因:

原因一是接口契约性:接口是本服务暴露给外部使用的,相当于上下游签了合同,如果随意修改合同,那么合同严肃性荡然无存。

原因二是版本兼容性:我们知道APP是有版本号的,假设APP版本1使用的是初始接口,这时APP版本2由于新业务需要使用新接口,但是你不能直接把初始接口改的面目全非,因为APP版本1有很多用户在用,如果只考虑新版本,那老版本将会报错。

原因三是时间窗口:我们知道APP发布版本是需要审核的,即使这一版本是强更,也会有一个时间窗口新功能和老功能是并存的,所以服务端接口在升级是必须考虑这种情况。

2 如何思考

既然不能直接升级,那么应该如何解决这个问题?我认为需要从两个维度思考:

  • 分层维度
  • 分端维度

2.1 分层思考

在代码结构落地实践中可以分为多层,但是核心还是三层,我们分别分析每一层如何适配接口变更:

  • 数据层
  • 业务层
  • 表现层

2.1.1 数据层

因为要考虑新老版本并存的问题,所以数据层必须保留新老版本所有字段,标记老字段标记为废弃,但是需要修改字段是否必填性,因为一些老版本字段变成不必填:

xml 复制代码
public class OrderCreateDO {
    private String orderId;
    private String bizColumnA;
    private String bizColumnB;
    @Deprecated
    private String bizColumnC;
    @Deprecated
    private String bizColumnD;
    private String bizColumnE;
    private String bizColumnF;
}

2.1.2 业务层

业务层是承载核心业务的层级,所以包含大量的业务逻辑,有两种方案:

  • 方案一:新增业务对象,重写业务方法
  • 方案二:修改业务对象,适配业务方法

方案一优点是不与老业务耦合,缺点是新老方法逻辑可能只有少部分不一样,所以需要复制老业务方法大量逻辑到新方法中。

方法二优点是可以复用老逻辑,缺点是新老逻辑耦合,如果适配逻辑没有处理好,可能会影响老逻辑。

所以两种方案有各自使用场景,方案一适用于业务逻辑重大变动场景,既然是重构所以可以重新声明新业务对象:

xml 复制代码
public class OrderCreateNewBO {
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnE;
    private String bizColumnF;
}

方案二适用于业务逻辑微调变动场景,所以老业务对象需要包含新老字段,标记老字段标记为废弃:

xml 复制代码
public class OrderCreateBO {
    private String bizColumnA;
    private String bizColumnB;
    @Deprecated
    private String bizColumnC;
    @Deprecated
    private String bizColumnD;
    private String bizColumnE;
    private String bizColumnF;
}

2.1.3 展示层

展示层不应该处理复杂业务逻辑,而应该是对业务对象的裁剪和适配,所以可以新增一个新版本接口,没有业务层那种负担:

  • 创建订单对象
xml 复制代码
public class OrderCreateDTOV2 {
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnE;
    private String bizColumnF;
}
  • 订单响应对象
xml 复制代码
public class OrderCreateResultDTOV2 {
	private String orderId;
    private String bizColumnA;
    private String bizColumnB;
    private String bizColumnE;
    private String bizColumnF;
}

但是如果展示层不规范,包含大量业务逻辑,那么思考方式和业务层一样,也需要使用方案二。

2.2 分端思考

一个系统在业务上通常分三个端:

  • 面向B端用户
  • 面向C端用户
  • 面向运营用户

这三个端都具有BFF层,但是通常实现技术不同:

  • 面向B端和C端有APP端
  • 面向运营端通常H5实现

所以如果不存在APP端,为了接口不要越来越臃肿,所以面向运营用户BFF层可以考虑删除老字段。

3 技术系统为什么复杂

《为什么需要生物学思维》这本书中提到复杂系统形成的四个原因:

  • 吸积
  • 交互
  • 必须处理的意外情况
  • 普遍的稀有事务

3.1 什么是吸积效应

从字面上解读可以将这一过程理解为吸附和积累。如同一粒粒沙子,每次加入一点点,最终汇聚成一座沙山。在软件开发的世界中,无论每一次的代码迭代在表面上看起来多么独立和微不足道,它们都在客观上促使代码量不断增长。

3.2 吸积效应带来哪些挑战

吸积效应导致代码变得如此庞大和复杂,以至于没有人能够全面掌握这个系统。当系统出现问题或故障时,最熟悉这部分代码的人可能早已离职,消失在人海中。

面对这种情况开发人员往往只能添加一段段兼容逻辑,以期降低对原有系统的影响。然而这种做法反而加剧吸积效应。甚至在某些无奈的情况下,团队可能被迫选择容忍某些错误,因为修复这些错误的代价远大于容忍它们所带来的风险。这就是吸积效应在软件开发中带来的巨大挑战。

3.3 怎么应对

技术系统都是往熵增方向发展,从有序发展到无序,所以工程师要通过一些手段减缓这个进程,常见方案是:

  • 统一技术架构
  • 统一技术规范
  • 统一业务语言
  • 代码分享与审查
  • 技术与业务分享
  • 系统稳定性建设
相关推荐
coderWangbuer21 分钟前
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
spring boot·后端·sql
攸攸太上26 分钟前
JMeter学习
java·后端·学习·jmeter·微服务
Kenny.志29 分钟前
2、Spring Boot 3.x 集成 Feign
java·spring boot·后端
sky丶Mamba1 小时前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端
feng_xiaoshi1 小时前
【云原生】云原生架构的反模式
云原生·架构
千里码aicood2 小时前
【2025】springboot教学评价管理系统(源码+文档+调试+答疑)
java·spring boot·后端·教学管理系统
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
liuxin334455663 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
数字扫地僧3 小时前
HBase与Hive、Spark的集成应用案例
后端
架构师吕师傅3 小时前
性能优化实战(三):缓存为王-面向缓存的设计
后端·微服务·架构