面向 Web 与移动客户端的 API 网关 vs BFF:权衡
API 网关 与 BFF:了解这两种模式如何影响版本管理、性能,以及对 Web 和移动应用的公有与内部端点分离。

问题:一个后端,多种客户端,需求在变化
常见的起点很简单:一个后端暴露一组端点,Web 应用和移动应用都去调用它们。看起来高效,因为只有一个地方去添加功能、修复 bug 和强制规则。
然后现实来了。Web 界面常常需要信息密集的页面、筛选、导出和管理操作。移动端通常需要更少的字段、更快的界面、支持离线的流程,并且要注意电量和流量消耗。即使功能是“相同”的,最适合每个客户端的 API 形态通常也并不相同。
随着时间推移,端点会发生偏移。Web 团队为新表格视图增加额外字段。移动端要求移除笨重的负载并将多个调用合并为一次。有人为“只给 iOS 用”添加了一个参数。还有人把内部的管理端点在公有应用里重用,因为“它已经存在”。起初干净的单一 API 慢慢变成了妥协的集合。
风险很快显现:
- 当某个客户端比另一个发布更快时会出现破坏性变更
- 大负载或过多往返使应用变慢
- 后端代码更复杂,因为每个端点都试图服务所有客户端
- 内部字段或操作意外泄露到公有 API
- 因为“看起来很小”的改动对老客户端并非小事,版本管理变得痛苦
这就是 API 网关 vs BFF 讨论背后的核心矛盾:你希望有稳定的公有 API 供移动和 Web 依赖,同时又要让每个客户端有空间按自己的节奏演进。
API 网关 和 BFF:是什么(以及不是什么)
API 网关是你 API 的前门。客户端调用网关,网关把请求路由到正确的后端服务。它通常处理你不想到处重复的共享关注点,比如认证检查、速率限制、请求日志和基本的请求成形。
后端为前端(BFF)是为某一特定客户端或客户端组(如“web”或“mobile”)构建的小型后端。客户端调用它对应的 BFF,BFF 再调用底层服务。关键理念是聚焦:BFF 可以使用客户端的“语言”(屏幕、流程、负载),即便核心服务保持更通用。
这些模式并不是替代良好领域服务或干净数据模型的方案。你的核心服务、数据库和业务规则应该仍然是事实来源。网关或 BFF 不应变成一堆业务逻辑的巨 blob,慢慢变成真正的后端。
区分它们的简单方式:
- 网关:一个入口点,处理共享关注点、路由和保护
- BFF:面向客户端的特定 API,减少客户端工作并隐藏内部复杂性
你也可以组合两者。常见做法是把网关放在边缘作为公有入口,然后在其后面为 Web 和移动分别部署 BFF。网关处理外层安全和流量规则,而每个 BFF 为其客户端塑形端点和响应。
在每种模式下请求路径如何变化
API 网关与 BFF 最大的区别在于你把“前门”逻辑放在哪:路由、认证检查和响应成形。
使用 API 网关时,客户端通常与一个共享入口通信。网关会把请求转发给内部服务,常做的工作包括令牌校验、速率限制和按路径路由。
使用 BFF 时,每种客户端类型(web、iOS、Android)调用为其专门构建的后端。该 BFF 再调用内部服务并返回为该客户端屏幕和约束量身定制的响应。
一个简单的请求路径示意:
- API 网关路径:Client -> Gateway -> Service(s) -> Response
- BFF 路径:Client -> BFF (web or mobile) -> Service(s) -> Response
所有权也常常发生转移。平台或基础设施团队通常拥有网关,因为它影响每个团队和每个服务。功能团队通常拥有 BFF,因为它跟随 UI 和发布节奏移动。
认证流通常是:客户端发送令牌,边缘层(网关或 BFF)验证或把令牌传给认证服务,然后把身份信息(用户 id、角色)转发给下游服务。不同点在于你在哪里应用客户端规则。网关通常施加通用策略;BFF 可以添加客户端特有的成形,例如为移动返回更小的负载或在慢网络上将多个服务调用合并为一次响应。
正是这个成形步骤让 BFF 很有优势,但这也意味着更多可部署且需保持一致的移动部件。
安全地分离公有与内部端点
公有端点是任何 Web 应用、移动应用、合作方或第三方可以到达的 API 路由。默认把它当作敌对的,因为你无法控制它穿过的网络或调用它的客户端代码。
内部端点则是系统内服务到服务的流量目标。它可以更频繁地变化、假定更多上下文并暴露更丰富的数据,但绝不应该直接从公网上可达。
使用 API 网关时,分离通常是物理且容易理解的:只有网关被暴露,它决定哪些外部路由存在。后面的所有东西保持私有。你可以让内部服务 API 更具表达力,而由网关强制一个更小、更安全的暴露面。
使用 BFF 时,分离更多是关于产品边界。每个客户端(web、iOS、Android)只与其 BFF 通信,BFF 再与内部服务通信。这样可以隐藏内部复杂性:BFF 可以调用三个服务、合并结果,并暴露一个简单响应,正好匹配客户端需要。
这种分离只有在你添加实际控制措施时才安全:
- 针对每条路由的精细授权:按角色和 scope,而不是一个“已登录”开关
- 公有端点按用户、令牌和 IP 的速率限制
- 负载过滤:只返回客户端需要的内容,剥离内部 ID、调试信息和仅管理员可见字段
- 明确的允许列表:哪些端点是公有的,哪些仅限内部使用
示例:移动应用需要“我的订单”页面。BFF 可以暴露 /orders,只返回订单状态和总额,而内部订单服务保留详细成本拆分和欺诈标记为私有。
版本管理:哪些更容易,哪些更困难
版本管理的痛点通常在 Web 与移动节奏不一致时显现。Web 团队可以在数小时内发布并回滚。移动应用可能需要几天或数周,因为应用商店审核和不更新的用户。这种差距是 API 网关 vs BFF 决策的实际驱动力。
使用 API 网关,你可以把版本放在一个前门(例如 /v1/...、/v2/...)。这易于解释和路由。缺点是,如果很多客户端和合作方集成坚持老版本,网关会变成版本博物馆,你不得不长期支持旧的数据形态。
使用 BFF,版本通常是“按客户端”的。移动 BFF 可以保留旧契约,而 Web BFF 前进更快,即便两者都调用相同的内部服务。这样通常减少了对公有版本长期维护的压力。代价是更多可变部件:你现在有多个版本决策需要管理和部署。
在服务内部做版本控制也可行,但它会把客户端驱动的变化推得很深,还可能使内部代码难以阅读,因为服务逻辑会根据客户端版本分支。
非破坏性改动是你的好朋友:添加可选字段、新端点或接受额外输入字段。破坏性改动包括重命名字段、改变类型(字符串变数字)或移除端点。
弃用最好是有计划地进行:
- 设定明确的日落日期并提前沟通
- 跟踪老版本的使用(日志、指标),注意拖延更新的用户
- 先推动客户端更新(尤其是移动),然后删除旧路径
- 当最终阻止旧版本时返回清晰的错误信息
性能:延迟、负载大小与调用次数
在 API 网关 vs BFF 的设置中,性能主要是一个权衡:系统内部多一跳与客户端网络上多次往返之间的权衡。通常最快的选项是减少客户端在慢、不稳定网络上的请求次数,即便这样会增加一小段服务器端开销。
当客户端本来会发起很多调用时,BFF 往往更有优势。与其让 Web 与移动分别调用五个端点并在设备端拼接结果,不如让 BFF 在服务器端获取所需并返回一次响应。这通常能降低移动端总延迟,因为每次蜂窝网络请求都会增加延迟。
常见的性能提升方式:
- 聚合:把多个服务的数据合并成一次响应
- 更智能的缓存:在边缘或 BFF 层对读密集界面进行缓存
- 更小的负载:只返回屏幕需要的字段
- 更少的往返:减少客户端的碎片化调用
- 统一的压缩与超时:在一个地方强制默认值
但这种模式也会带来负担。每增加一层都增加 CPU 工作并增加等待点。如果为 Web 与移动重复实现相同的聚合逻辑,你可能会重复工作并导致行为不一致。过度获取也是常见损失:一个试图满足所有界面的通用端点可能返回很大的负载,浪费时间与带宽。
移动环境使这些权衡更为尖锐。网络不稳定意味着重试与超时是常态,每多一次调用都会消耗电量。冷启动也很重要:如果应用在首屏可用前需要多次请求,用户会感到迟缓。
一个实用规则是:先优化减少客户端请求,再优化新增的那一跳。
评估设计的快速方法
如果一个屏幕需要超过 2-3 次顺序调用,考虑做聚合。如果响应很大且大部分未被使用,考虑拆分端点或为客户端定制 BFF。
运维:部署、监控与扩缩容
在 API 网关 vs BFF 的问题上,关键的运维问题是你愿意运行和支持多少个可动部件。网关往往成为许多团队和客户端的共享基础设施。BFF 通常是较小的服务,但你可能会为每个客户端(web、iOS、Android、合作方)维护一个,从而增加发布和 on-call 的负担。
在测试和发布方面,当你主要需要路由、认证和速率限制时,网关往往更安全。变更集中化,因此一次失误可能影响所有人。BFF 将影响面缩小:Web 专属的变更只发布到 Web BFF,而不会影响移动端。代价是需要维护更多的 CI/CD 管道和更多并行版本。
可观测性上,你需要能够在各层看到单个用户请求,特别是当一次移动调用触发多次后端调用时。建议:
- 使用关联 ID,并在网关、BFF 与后端日志中传递它
- 捕获追踪以找出耗时在哪里(网关、BFF、下游服务)
- 保持结构化日志(客户端、端点、状态码、延迟、负载大小)
- 为每个端点跟踪关键指标:错误率、p95 延迟、吞吐量
扩缩容也不同。网关是共享瓶颈:它必须能处理流量峰值和热点端点而不成为阻塞。BFF 允许按客户端扩展,这在 Web 流量白天激增而移动稳定时很有用。
在事故中,故障表现也会移动。使用网关时,问题通常表现为广泛的 5xx 或认证失败。使用 BFF 时,问题可能只影响某个客户端的功能。写清楚运行手册,明确首先查看的位置,并保持降级策略简单(例如在超时情况下返回精简响应)。
如何选择:一个简单的逐步决策流程
在 API 网关 与 BFF 之间选择,更多是基于客户端的日常需求而不是理论。
实用决策流程
-
从客户端开始,而不是从服务器。写下你支持的每个客户端(Web 应用、iOS、Android、合作方 API、内部管理)并列出每个关键界面需要的内容。如果同一个“客户详情”视图在不同客户端需要不同字段或不同调用模式,这就是需要客户端特定成形的强烈信号。
-
映射当前状况。把你现有的端点标记为共享的(如订单、支付、用户等核心领域操作)与为展示层塑形的(如仪表盘摘要、合并的“首页”负载)。共享部分属于核心后端。面向展示的部分通常更适合放在 BFF。
-
决定成形与规则应该放在哪里。如果你主要需要路由、认证、速率限制、缓存和安全地暴露公有/内部端点,网关是自然之选。如果你需要真正的组合(调用多个服务,把六次调用变成一次、为不同应用提供不同负载),把这些逻辑放在 BFF 里,以保持可测试和可读。
-
选择你能遵守的版本和弃用规则。例如:“没有新版本不得做破坏性改动,所有弃用字段保留 90 天。”使用纯网关方案时,你可能会在公有层做版本转换。使用 BFF 时,你通常可以把核心 API 保持稳定,仅对每个客户端的 BFF 端点做版本控制。
-
计划发布并度量。在更改前捕获基线指标:p95 延迟、每屏调用次数、负载大小和错误率。先对小比例用户发布,比较前后再扩大范围。
一个简单示例:如果移动应用每月发布一次,而 Web 门户每天发布一次,一个小型的移动 BFF 可以保护移动端不受后端频繁变动影响,而 Web 客户端继续快速迭代。
示例:Web 门户 + 发布节奏不同的移动应用
想象一家公司有 Web 客户门户和移动字段应用。两者都需要相同的核心数据:客户、作业、发票和消息。门户每周变更,移动应用发布较慢,需要通过应用商店审核并且需要在弱信号下工作。
问题会很快显现。移动用户需要紧凑的响应、更少的调用和支持离线工作的流程(例如一次下载当日任务,然后稍后同步更改)。Web 门户可以接受更多调用和更丰富的界面,因为它几乎总是在线且更容易更新。
选项 A:网关在稳定服务前
网关优先的选择保持后端服务大体不变。网关处理认证、路由以及像 headers、速率限制和简单字段映射之类的小调整。
版本管理主要在服务 API 级别。这有利于减少可变部件,但也意味着移动特定的需求常常把你推向像 /v2 这样的广泛版本,因为底层端点是共享的。
如果把网关当作唯一的公有门,端点暴露会更清晰。内部端点保持私有,但你必须严格控制网关能访问和发布的内容。
选项 B:为移动量身的 BFF
使用移动 BFF 时,移动应用与为移动屏幕和同步流程设计的端点通信。BFF 可以聚合数据(作业详情 + 客户 + 最近消息)、裁剪字段,并返回符合应用需求的一次性负载。
会发生的变化:
- 版本管理按客户端更容易:你可以给移动 BFF 版本,而不强制 Web 门户跟随
- 移动性能通常改善:更少的往返和更小的响应
- 公有与内部分离更清晰:BFF 是公有的,但它调用的内部服务无需暴露
常见错误与陷阱
API 网关 vs BFF 争论中最大的陷阱是把网关变成小型后端。网关擅长路由、认证、速率限制和简单请求成形。当你把业务规则塞进网关,就会得到难以测试、难以调试并且容易在配置变更时破坏的隐藏逻辑。
BFF 有相反的错误:团队为每个界面或功能都创建一个 BFF,维护工作量爆炸。BFF 通常应映射到客户端类型(web、iOS、Android)或明确的产品区域,而不是每个 UI 视图。否则你会在十几个地方重复相同规则,版本管理变成全职工作。
版本管理错误常来自极端做法。如果你在第一天就对一切都版本化,你会过早冻结 API 并让旧变体永远存在。如果你从不版本化,最终会不小心推出破坏性变更。一个简单规则:对于小的添加性改动不要版本化;当你移除、重命名或改变含义时才版本化。
公有 vs 内部端点是团队受伤最深的地方。将内部服务直接暴露到互联网上(即使“临时”)会把每一次内部变更变成潜在的故障或安全事件。保留清晰界限:仅网关或 BFF 对外可见,内部服务保持私有。
性能问题通常是自找的:过大的负载、过多的往返以及没有延迟预算。例如,移动端可能只需要订单状态和总额,但却拿到包含每条明细和审计字段的完整订单对象,使得每次请求在蜂窝网络上都很慢。
需要警惕的信号:
- 网关配置中引用了像“退款资格”或“VIP 规则”这样的业务概念
- BFF 的数量增长速度快于它所服务的客户端
- 你无法用一句话解释你的 API 版本化策略
- 内部服务端点可以从公网上访问
- 响应持续增长,因为“也许以后会有用"
快速清单与下一步
如果你在 API 网关 vs BFF 的抉择上卡住了,关注会首先在现实中破坏的点:发布、负载和安全边界。
快速清单
如果你对以下问题有多个“否”,随着客户端增长,你当前的设置可能会造成问题:
- 你能否在不强制每个客户端在同一周更新的情况下更改一个后端服务?
- 公有端点(对互联网安全)与内部端点(仅限受信任系统)之间有明确边界吗?
- Web 与移动端是否只收到它们需要的内容(而非一个巨大的“万用锅”响应)?
- 你能否逐步放量发布(小比例先行)并快速看到错误、延迟和异常流量?
- 你知道谁拥有每个端点的契约以及谁批准破坏性变更吗?
下一步
把答案转化为计划。目标不是完美架构,而是减少你发布时的意外。
用通俗语言写下 API 契约(输入、输出、错误码、允许变化的范围)。选择一个归属模型:谁负责客户端需求(web/mobile),谁负责核心领域服务。决定版本管理的位置(按客户端或集中)并设定你会遵守的弃用规则。
在大规模重构前先加上基本监控:请求率、p95 延迟、错误率和按负载大小排序的端点。先对最具风险的客户端流程做原型验证。
如果你在用 AppMaster (appmaster.io) 构建,一个实用做法是把核心业务逻辑和数据模型保留在生成的后端,然后仅在客户端确实需要不同负载成形或发布隔离时,添加薄薄的网关或 BFF 层。
如果清单中的问题难以回答,把它视为信号:在添加更多端点前,先简化契约并收紧公有与内部的边界。
常见问题
当你主要需要一个统一的公有入口,处理认证检查、速率限制和路由等共享控制时,先从 API 网关开始。当 Web 与移动端确实需要明显不同的负载、减少客户端请求或独立的发布节奏时,再增加 BFF。
网关适合放置跨切关注点和边缘流量控制,所以把路由、认证强制和基本的请求/响应处理放在网关。BFF 则负责面向客户端的组合逻辑,例如合并多个服务调用、裁剪字段等。但仍然不要把核心业务规则堆到 BFF 上。
网关提供一个版本化的“前门”,易于解释,但可能需要你长期支持旧版本。BFF 则允许按客户端单独版本化:移动端可以保持稳定,而 Web 快速前进,代价是需要维护更多服务和合约。
默认优先非破坏性改动,例如添加可选字段或新端点。只有在你移除字段、重命名字段、改变类型或改变字段语义时,才创建新版本,因为移动用户可能数周不升级。
把内部服务保持私有,只把网关或 BFF 暴露到公网上。过滤响应以确保客户端只得到所需内容,并为每条路由强制单独授权,避免仅凭“已登录”就能调用管理操作。
当客户端本来要发起多次顺序调用时,使用 BFF 更好:服务器端聚合通常比移动端的多次网络往返更快。网关会增加一层跳转,所以要保持轻量并测量延迟与负载大小,避免隐藏的变慢。
网关是共享的瓶颈:配置或故障可能影响所有客户端。BFF 将变更影响范围缩小到单一客户端,但你要运行更多的部署、更多的监控并承担更多的 on-call 责任。
通过关联 ID 把请求穿透网关/BFF 到所有下游服务,这样可以把一次用户动作端到端追踪清楚。对每个端点追踪少量关键指标:错误率、p95 延迟、吞吐量和负载大小,能让性能回归快速暴露。
常见陷阱是让网关积累业务规则,这会使行为难以测试并且容易因配置变更而出错。另一种常见错误是为每个页面都建一个 BFF,导致维护量爆炸。保持 BFF 与客户端类型或清晰的产品域一致,而不是每个视图一个服务。
把核心数据模型和业务流程保留在生成的后端里,只在真正需要为客户端做负载裁剪或发布隔离时,添加薄薄的一层网关或 BFF。在 AppMaster 里,这通常意味着在生成的 Go 后端中保持稳定的领域端点,仅为移动友好的聚合或裁剪添加小层。


