文章

不要迷信'一个接口解决多数需求'

不要迷信'一个接口解决多数需求'

1.背景

想起之前工作中,有见过下面这种接口

1
GET /api/list

参数非常丰富:

1
2
3
4
5
6
7
8
9
10
11
12
type
status
scene
sort
order
page
page_size
start_time
end_time
keyword
include_xxx
exclude_xxx

甚至还有:

mode = admin / user / audit

设计的初衷:反正数据都在一张表里,多加几个参数就能支持了。

或者说:这个接口比较通用,先往这里加吧。


2.为什么这么写?

  • 少写接口
  • 少维护路由
  • 前端方便,一个接口啥都能搞定

而且在项目最初阶段,数据量不大,使用场景不多,业务逻辑也不复杂,所以常常让人有一种感受:

"只要参数设计得够灵活,这个接口可以一直使用下去"

3.问题开始出现

随着项目的迭代,这个接口承载的需求开始变得多样:

  • 首页列表
  • 管理后台列表
  • 搜索页结果

表面上,这些需求都是在“查列表”,但语义已经开始逐渐出现偏差;还是以上面👆list接口为例子:

  • 首页列表:要快,要分页,要排序,要分页
  • 管理后台:可以接受慢,但是要全,要有不同的排序,不同的分页逻辑
  • 搜索页结果:不同分页逻辑,不同排序逻辑

于是,代码开始出现下面这种东西:

1
2
3
4
5
6
7
8
switch scene {
  case "user":
    //...
  case "admin":
    //...
  case ...:
    //...
}

或者:

1
2
3
4
5
6
7
if needCount {
  //...
}

if needDetail {
  //...
}

此时这个接口已经不再是通用的接口,而是“参数驱动”的逻辑分发器,也就是说,程序员在“用参数模拟接口”。


4.错误的解决办法:把不好区分的业务逻辑拆成新接口

这个时候开发要开始想着解救了,策略往往是:

1
"既然这个接口不能再往里塞东西了,那我们将新需求写成新接口就好了,老的别动。"

这属于没有办法的办法,但是依旧没有解决“被整合”的接口的冗余问题,且还会引入新的问题:

1
为什么你一些list接口承载了A,B,C的混合场景,有些又只承载了D的业务场景。

接口逻辑越来越混乱,难以维护的项目代码就从这一步开始慢慢形成了。


5.宁愿多几个接口,也不要让一个接口的语义膨胀

因此,在接口设计时,我们就应当遵循一个策略:

宁愿多几个接口,也不要让一个接口的语义膨胀。

比如:

1
2
3
GET /api/home/posts
GET /api/admin/posts
GET /api/export/posts

它们可能是查同一张表,但是:

  • 查询条件不同
  • 超时与限流策略不同
  • 缓存策略不同

当某个业务场景需要变动,需要维护时,只需要调整对应业务的接口,而无需变动其他接口。 每个接口,都应该有一个“说得清楚”的使用场景。


6.结语:接口数量多 > “万能接口”

很直接的判断标准,当以下情况出现时,我们就应该警觉:

  • 参数出现mode/scene等
  • 文档里写着:“不同场景下行为不同”
  • 性能问题只能靠“限制参数”解决

当接口数量多时,问题是“管理成本”;但当万能接口出现问题,出现的是“系统不可控”。

前者往往是显性的、可治理的,而后者是隐性的,往往是在系统复杂后才爆炸的。


“一个接口解决所有需求。”听起来很工程化,但前提是所有需求,在本质上都是同一个

而现实往往不是这样。

本文由作者按照 CC BY 4.0 进行授权