不要迷信'一个接口解决多数需求'
不要迷信'一个接口解决多数需求'
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 进行授权