BFF:为前端整理后端能力
BFF:为前端整理后端能力
一个订单详情页可能要分别查询订单、商品、优惠和配送信息。直接在浏览器里调用这些接口并没有错,但页面会逐渐知道越来越多后端细节:服务怎么拆分、字段从哪里取、某个接口失败后还能不能继续展示。
换成移动端后,问题又多了一层。它需要的数据更少,对请求次数和载荷大小却更敏感。让桌面端、移动端和第三方客户端共用一套越来越臃肿的接口,最后通常是谁都用得不顺手。
BFF(Backend for Frontend)就是放在客户端与后端服务之间,专门为某类前端整理数据和调用方式的一层服务。这个模式关注的不是“多加一层后端”,而是把原本散落在客户端里的服务编排收回到一个明确边界中。Sam Newman 对 BFF 的介绍强调的是按用户体验拆分后端,而不是让所有客户端继续迁就同一个通用接口。
BFF 面向的是一类前端体验
BFF 对外提供的是页面或客户端容易使用的契约,对内再调用一个或多个领域服务。前端不必知道订单来自哪个服务、用户头像需要经过怎样的协议转换,也不需要为一次页面展示维护一串相互依赖的请求。
这里的 “Frontend” 通常指一类体验,而不是每个页面都配一个后端。Web 管理台和移动应用的数据形态差异很大,可以各自拥有 BFF;同一站点里的订单页和个人页如果使用方式接近,没有必要为了对应页面而拆成两个服务。
拆分的尺度更适合跟着团队所有权和客户端差异走。否则 BFF 数量会随着页面增加,部署、权限和排查成本也会一起增长。
哪些工作适合放进 BFF
最常见的是数据聚合。BFF 并行请求订单、商品和配送服务,再返回页面需要的结果。浏览器少处理几次网络往返,也不再直接依赖内部服务的拓扑。
另一类工作是数据整形。后端模型往往围绕领域设计,页面更关心能直接展示的视图模型。字段重命名、单位和时间格式转换、过滤客户端用不到的数据,都可以在这一层完成。
协议适配也很自然。内部服务可能使用 gRPC 或消息系统,而浏览器更适合通过 HTTP、SSE 或 WebSocket 访问。BFF 可以把内部协议转换为客户端能使用的接口,但不应该把底层协议里的所有概念原样泄露出去。
认证流程有时也会经过 BFF。例如浏览器只持有安全 Cookie,由 BFF 管理访问下游服务所需的令牌。不过它只是凭证和调用边界,不能取代领域服务里的授权判断。订单是否允许退款,仍然应该由拥有订单规则的服务决定。
业务规则不要搬到这一层
BFF 很容易变成一个方便放代码的地方。刚开始只是拼接字段,后来加入价格计算、库存判断和订单状态流转,最后它就成了另一个领域服务,而且规则还可能与原本拥有这些能力的服务重复。
一个实用的区分是:与展示方式有关的逻辑可以留在 BFF,与业务事实有关的逻辑应回到对应领域。把多个服务的结果整理成订单卡片属于前者;决定订单能否取消属于后者。即使以后没有这个前端,业务规则也应该继续成立。
BFF 也不适合只做无差别转发。如果大部分接口只是换一个地址再原样透传,请求并没有变得更适合前端,却多了一次网络跳转和一套需要维护的服务。
它和 API Gateway 解决的问题不同
API Gateway 更接近整个系统的统一入口,通常处理路由、TLS 终止、限流、访问日志和通用认证等横切能力。BFF 则知道某类客户端需要什么,会参与数据聚合、字段转换和降级处理。
两者可以同时存在:客户端先经过 API Gateway,再进入自己的 BFF;BFF 随后调用领域服务。规模较小时,它们也可能部署在同一个进程中。是否独立部署不是判断标准,关键要看其中的逻辑是在服务所有客户端,还是只在适配某一种前端体验。
Microsoft 的 BFF 模式说明也提到了这层边界:通用能力不宜在多个 BFF 中反复实现,否则拆分客户端差异的收益会被重复代码抵消。
聚合请求也会放大故障
浏览器从五个请求变成一个,不表示系统里只剩一次调用。BFF 仍然要访问五个下游,而且整体耗时通常受最慢的那个接口影响。聚合得越多,超时、部分失败和资源占用就越需要明确处理。
设计接口时要先区分必要数据和可选数据。订单主体失败时,页面可能无法继续;推荐商品失败时,返回订单并省略推荐通常更合适。BFF 应给整次请求设置时间预算,将取消信号传给下游,并避免在客户端已经断开后继续执行没有意义的调用。
错误也不能全部压成同一个“系统异常”。前端至少要分得清认证失效、输入错误、资源不存在和暂时不可用。对于部分成功的聚合结果,还要约定缺失字段、错误信息和页面降级之间的关系。
面向页面的契约仍然需要稳定
BFF 可以吸收后端变化,但不意味着它的接口可以随页面代码随意变化。前端依赖的是这一层的字段、错误和缓存语义,它们同样需要版本兼容和契约检查。
直接把下游响应拼在一起返回最省时间,却会让内部模型重新暴露给客户端。更合适的做法是定义自己的响应模型,只返回当前体验需要的数据。上游字段调整时,变化先在 BFF 内部消化,不必一路传到组件。
缓存也要按聚合后的语义考虑。不同下游数据的更新时间、用户权限和失效条件可能完全不同,不能因为其中一个接口可缓存,就默认整个响应都能共享缓存。
运维成本不会因为服务靠近前端而消失
BFF 是后端服务,需要部署、扩容、告警和安全维护。它的日志至少要能把一次客户端请求与所有下游调用串起来,并看出时间花在哪个服务、哪些调用被取消、最终是否采用了降级结果。
如果由前端团队维护,团队也要承担这部分值班和容量责任。BFF 的价值来自客户端契约可以独立演进;如果修改仍然要跨多个团队排期,而服务本身又无人负责,这一层只会增加交接点。
什么时候值得引入
当不同客户端确实需要不同的数据形态,或者一个页面不得不理解多个后端服务时,BFF 往往能让边界变清楚。内部协议不适合直接暴露给浏览器、前端发布节奏经常受通用 API 限制,也都是可以考虑它的信号。
反过来,如果系统只有一种客户端,后端已经提供了合适而稳定的接口,直接调用通常更简单。先确认前端正在承担的聚合和适配成本,再决定要不要把它移到服务端;不要只因为架构图里缺少这一层就增加 BFF。