添加新评论

接口谁设计,锅就谁背

前后端接口设计

上个月做一次产品列表页改版,后端同事给了我一套“标准RESTful接口”。数据结构很规范,Swagger文档也齐全。但拿到手一看——一个产品对象嵌套了五层,包含47个字段,前端实际用到的只有12个。我需要从 data.product.basicInfo.displayConfig.title 这种路径里抠数据,再拼成UI需要的格式。一个产品卡片组件,300多行代码,有120行在做数据转换。

这种事在前端开发里太常见了。大部分前端工程师拿到后端接口后的第一反应不是“这个接口设计得好不好”,而是“我怎么写adapter把数据转成我要的格式”。写多了就麻木了,觉得这就是前端该干的活。

但仔细想想:为什么前端要替后端做数据组装?为什么不能是后端按前端的需求来设计接口?

BFF,听起来很美

大概五六年前,BFF(Backend for Frontend)模式开始流行。思路是前端自己搭一层Node.js中间层,后端只提供基础的数据访问接口,前端自己组装、裁剪、聚合数据。听起来挺好——前端终于掌握了数据层的主动权,再也不用看后端脸色了。

我当时对这个方案很兴奋。终于不用在后端的接口格式和UI需求之间做翻译了。我们花了两周搭了一个Node.js的BFF层,用GraphQL做数据查询,心想这下前端可以想要什么数据就拿什么数据。

结果呢?

跑了一年多回头看,BFF层里80%的代码在做透传——后端返回什么,BFF原样转给前端。真正有业务价值的聚合逻辑大概只有15%。剩下的5%是一些字段名映射和null值处理。

更尴尬的是性能数据。加了BFF之后,列表页的首屏时间增加了40毫秒。BFF本身的处理很快,大概5到8毫秒,但多了一跳网络开销。运维同学还来找我们:“你们为什么要多加一层转发?直接调后端不好吗?”我当时的回答是“为了前端数据聚合”,但心里清楚——那15%的聚合逻辑,完全可以让后端直接做。

透传代理的陷阱

BFF变成了透传代理之后,还带来一个更隐蔽的问题:维护成本。后端加了一个新字段,BFF的透传逻辑没同步更新,前端还是拿不到数据。这种“中间层漏传”的问题排查起来特别费劲,因为大家第一反应不会怀疑BFF——“它不就是透传吗,怎么会漏?”

我后来留意了几个不同团队的BFF实现,发现这个模式非常普遍:BFF的初衷是“前端自定义数据层”,但落地时变成了“多一层代理”。原因也不复杂——前端团队通常没有足够的动力去维护一个真正的数据聚合层。BFF需要理解后端的微服务结构、知道数据分散在哪些服务、处理服务间调用顺序和错误传播。这些工作跟写前端组件完全是两回事,大部分前端工程师既没时间也没兴趣去做。

还有一个我见过的场景:前端团队在BFF里写了大量业务逻辑——用户等级计算、产品标签判断、推荐排序权重。后端团队不认这些逻辑,说“这不是我们的代码,出了问题我们不排查”。后来出了一个线上bug,用户等级计算有误,前端说是BFF的逻辑,后端说那是你们自己写的跟我没关系。排查花了两天,最后发现是BFF里的等级计算逻辑跟后端的规则引擎差了半个版本。

真正改变的不是技术,是所有权

后来我们调整了协作方式。不是换技术方案,而是换了所有权结构:前端定义API契约——用TypeScript interface写出期望的数据格式和字段语义,后端来实现。变更时双方一起review。BFF退化成纯数据管道,不做任何业务逻辑。

这个调整花了大概三个月才推下去。后端团队最初的阻力不小——“你们前端凭什么定义接口格式?”我们的理由很具体:你们给的接口有三个端在用,Web端、App端、小程序端,三个端的需求不一样,你们不可能设计出一个通用接口满足所有人。不如我们每个端自己定义期望的格式,你们负责实现。

上线后联调时间从平均两天降到半天。大部分格式问题在契约review阶段就暴露了,不用等到联调时才发现。BFF从“业务逻辑层”降级成“数据管道”之后,代码量减少了70%,维护成本大幅下降。

这个过程让我意识到一件事:BFF是否成功,跟技术方案关系不大,跟组织关系更大。如果前端和后端归同一个技术负责人管,BFF更容易成功,因为负责人能协调两边的API设计。如果前端和后端各管各的,BFF就容易变成扯皮地带——任何schema变更都要走跨团队审批,比直接对接后端还慢。

什么时候该加一层,什么时候不该

看了这么多团队的实践,我慢慢形成一个判断:当后端API的消费者只有Web前端一个端时,加BFF通常是过度工程化。后端直接为前端设计接口就好,中间加一层只是增加延迟和排查成本。

但当API同时服务Web、App、小程序时,每个端的数据需求差异大,一个通用接口很难满足所有场景,这时候加一层前端可控的聚合层才有意义。

还有个反直觉的发现:我在金融业务里观察到,对数据实时性要求高的场景(比如持仓页、行情页),加BFF反而是负优化。这些场景下前端需要的是WebSocket直连后端推送,中间多一层就多一份延迟。但很多团队为了“架构统一”,把持仓数据也走BFF的REST接口,白白损失了几十毫秒的实时性。

GraphQL Federation 解决的是同一个问题吗

最近两年GraphQL Federation在技术社区热度很高。它的思路是每个微服务定义自己的subgraph,由gateway自动组合成统一schema。本质上就是把BFF的职责从“手动聚合”变成“自动组合”。

这个方向听起来对,但门槛不低:需要团队全面迁移到GraphQL、需要额外的schema治理流程、gateway本身的性能开销也需要关注。大部分团队其实不需要走这么远。在现有REST API上加一层轻量的API Gateway,用OpenAPI定义契约、用代码生成工具自动生成前端SDK、CI/CD自动检测兼容性变更——这套组合拳不新鲜,但做到的团队可能不到一成。大部分前端团队仍然在手动写axios调用,联调时的问题靠猜和console.log来排查。

说实话这个问题我想了很多年,也没找到一个标准答案。接口设计这件事,说是技术问题,不如说是组织问题——谁定义、谁维护、谁为质量负责,这些问题的答案取决于团队结构,而不是技术选型。康威定律在这里体现得特别明显。

上周review代码的时候发现,我五年前写的那个BFF层还在跑。代码注释里有几个TODO到现在还没完成。所以这篇里说的判断,大家看看就好,我也在边做边改。

You voted 1. Total votes: 1