API不是积木:为什么我反对一切‘可组合性’崇拜

🔑 关键词:API设计,可组合性,接口契约,服务耦合,工程实践

📖 摘要:本文从工程现场出发,批判当下对API可组合性的盲目崇拜,提出接口设计应当优先考虑业务语义完整性与故障隔离,而不是机械的复用。

在到处都在教你怎么把API做得像乐高积木的年代,我想泼一盆冷水。

图片

过去六年我参与过三个大型中台项目的接口治理,见过太多所谓的“可组合API”——一个订单接口被拆成creatOrder、updateOrderStep1、submitOrderStep2、addOrderItem、removeOrderItem……每个接口都又小又干净,但前端每次下单要调六次,中间还有依赖顺序。后端团队很自豪,因为复用率很高;前端同学却在深夜骂娘,因为一次网络闪断就不知道订单落在哪个中间状态。

图片

我不想否定复用本身,但“可组合性”被当成最高评判标准,本身就是一种偷懒。我们拿微服务里的名词当咒语,却忘了API的终极使命是忠实地覆盖一个业务操作——它有开始、结束、成功、失败,甚至应该有补偿语义。真正算得上组合的是业务编排,而不是把一次事务解构成一堆无状态的HTTP动词。

图片

我见过一个比较清醒的做法。某支付团队在设计退款接口时,没有拆成verfiyOrder、calcRefundAmt、execRefund、notifyMerchant四个接口,而是直接定义了一个refund(): RefundResult,内部自带校验、计算、执行和状态回滚。同时提供queryRefundStatus和retryRefund。外部调用方只需要跟一个接口打交道,业务日志里也能看到完整的追踪链。有人说这个API太大、不够原子,但它的原子性是业务层面的,不是代码函数层面的。

从可组合性的角度,你当然是正确的;但从工程系统的角度看,你只是把出错的机会从一个黑洞变成了七个黑洞。故障隔离是API更稀缺的属性——一个接口出了问题,至少它可以被单独降级,而不会因为组合顺序导致雪崩。可组合性往往意味着强耦合的调用方逻辑,问题在线下永远测不出来,到了线上压力下就暴露无遗。

图片

另一个常被忽略的维度是版本演进的成本。可组合API吹嘘的灵活性,恰恰是版本管理的噩梦。你拆出一个paymentCore,三个月后业务需求变了,最后发现要改的接口涉及上游17个组合场景。但如果你保持一个足够清晰的业务接口,改动的边界也足够清晰——面变小了,回归成本就低。API不是积木,它更像一个门把手,人们关心的是转动的手感,而不是里面螺丝的可替换性。

图片

我现在更倾向于那种看起来有点“笨”的API:操作粒度跟业务事件一一对应,错误码覆盖真正的故障场景,参数里带着上下文而不需要调用方自己去拼凑。开发新接口时,我会先问自己:如果调用方只读文档,不看我的实现,他能猜到我支持的组合方式吗?如果不能,那大概率是我在设计上过度聪明了。

图片

当然,我并不是说所有API都该胖成巨石。读接口、面向内部的辅助接口,组合一些没问题。但面向关键链路、涉及资金或主流程的API,请给业务语义多一点尊重。可组合性只是手段,区分“组合”和“割裂”的,永远是那个被你藏在文档底部的状态机。

🏷️ 标签: