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

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 怎么应对

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

  • 统一技术架构
  • 统一技术规范
  • 统一业务语言
  • 代码分享与审查
  • 技术与业务分享
  • 系统稳定性建设
相关推荐
Victor35628 分钟前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易28 分钟前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧34 分钟前
Range循环和切片
前端·后端·学习·golang
WizLC37 分钟前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor35644 分钟前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法1 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
Solar20251 小时前
TOB企业智能获客新范式:基于数据驱动与AI的销售线索挖掘与孵化架构实践
人工智能·架构
白宇横流学长1 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
Python编程学习圈2 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端
bing.shao2 小时前
Golang 高并发秒杀系统踩坑
开发语言·后端·golang