mcpField
B
bff-design

bff-design

github.com/citypaul开发76060 次安装4 周前更新
代码仓库

关于 bff-design

帮助前端团队决定是否采用 BFF、如何划分与治理 BFF 的架构技能。内容聚焦于体验归属、职责边界、上游聚合韧性和用户身份传递,避免 BFF 演变为共享公共 API 或业务决策层。

主要功能

  1. 01评估采用时机与 BFF 数量
  2. 02按产品体验划分所有权
  3. 03界定展示整形与业务决策
  4. 04设计并行聚合及部分失败策略
  5. 05规范向上游传递用户身份

适用场景

  1. 01判断单页应用是否需要 BFF
  2. 02为多个客户端体验确定 BFF 边界
  3. 03设计上游服务故障时的降级响应
  4. 04避免跨体验共享 BFF 导致接口固化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/citypaul/.dotfiles --skill bff-design

安装后在 Claude Code 中输入/bff-design确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
adoption-and-granularity.md
aggregation-and-resilience.md
alternatives.md
upstream-identity.md

详细介绍

BFF 设计

后端前端化(BFF)首先是一种所有权模式,而非一项技术:客户端应用使用的 API 是该应用的一部分,由负责前端的团队所有,且不会被其他应用使用(该模式的创始人 Calçado)。从架构上看,它是面向某种用户体验的驱动主机,负责塑造、聚合和调解;但不负责决策。

本技能负责模式层面的决策:是否采用 BFF、采用多少个、各自可以负责什么、如何聚合并应对上游故障,以及如何将用户身份传递给上游服务。加载 bff-entry-points,以保护 BFF 自身边界——访问分类、预先配置的注册器、会话/CSRF、实时通信保护、强制执行门禁;本技能不会重复这些内容。加载 structure-codebase,了解物理 endpoints/ 目录树;加载 hexagonal-architecture,了解驱动主机的定位;加载 api-design,了解如何处理不受你控制的消费者所使用的契约(健康的 BFF 从来不会有这类消费者);加载 secure-oauth-oidc,了解上游身份参考中所有内容背后的 OAuth/OIDC 协议不变量。

做出决策前,请阅读相关参考资料:

  • 在决定是否采用、运行多少个 BFF、由谁负责、如何处理移动端版本偏差,以及如何选择可部署服务或模块的规模时,请阅读 references/adoption-and-granularity.md。
  • 当 BFF 向上游服务扇出请求时,请阅读 references/aggregation-and-resilience.md:其中介绍组合、部分故障、超时/重试预算和缓存。
  • 当 BFF 代表用户调用上游服务时,请阅读 references/upstream-identity.md:其中介绍令牌交换、缩小范围、防止混淆代理问题,以及撤销传播。
  • 在权衡 BFF 与共享网关、GraphQL 联邦、令牌中介后端、直接从 SPA 调用 API,或元框架服务器时,请阅读 references/alternatives.md。

你需要 BFF 吗?

信号解读
即将增加第二种客户端类型(移动应用、合作伙伴界面、电视)最强的采用信号——Newman:从一开始就考虑为每个参与方设置一个 BFF
一个页面/屏幕需要扇出调用许多下游服务聚合应放在服务附近的服务器端,并针对每种体验进行定制
前端团队每新增一个端点都要排队等待通用 API 团队处理SoundCloud 遇到的问题——BFF 将契约转移到前端团队内部
浏览器应用处理敏感/个人数据(默认情况下,任何需要登录的应用都符合条件)IETF 浏览器应用 BCP 轨道的指南仅基于安全考量,就强烈建议采用 BFF 架构——令牌保留在服务器端
只有一个界面,且只向一个后端发出简单、相似的请求不要采用——这是 Azure 明确指出的“不适用”场景——除非触发了另一行条件:各行回答的是不同问题,而仅安全这一项就足以为处理敏感数据、仅连接单个后端的 SPA 提供采用 BFF 的理由(限制令牌无需以聚合需求为前提)
已在运行带有前端专属解析器的 GraphQL,或使用元框架服务器(Next/Remix/SvelteKit)你可能已经拥有一个 BFF——将本技能应用于现有 BFF,而不是再增加一个组件

元框架服务器就是 BFF:Remix 明确将其服务器描述为 BFF,Next.js 路由处理程序也承担相同的职责(并面临同样的风险——默认情况下,它们可从公开网络访问,因此 bff-entry-points 同样适用于它们)。

一种体验,一个 BFF

粒度单位是体验——一个连贯的产品界面,无论它由哪些平台呈现。渲染同一种体验的 iOS 和 Android 听众应用是一个消费者,有两个构建版本,而不是两个消费者;创作者应用则是另一种体验,应拥有自己的 BFF(这是 SoundCloud 的做法,也符合 Newman 的原则)。

  • 体验决定默认数量。团队结构可以覆盖这一默认值:即使体验几乎相同,iOS 和 Android 团队分开也可能足以支持单独的 BFF,因为这种模式的价值在于由团队掌控自己的节奏(Conway)。一种体验由两个团队提供服务,说明所有权划分存在问题——应指定一个负责团队,或接受拆分;不要让多个团队共用同一个 BFF。
  • 前端团队拥有自己的 BFF:语言、发布节奏和优先级均由其决定。由后端平台团队拥有的 BFF,会重建这种模式旨在消除的协调队列。
  • BFF 和前端是一个逻辑部署单元:条件允许时放在同一仓库中,让契约共同演进,而不是分别版本化。版本管理的严格程度取决于消费者的可追溯性——采纳参考资料会说明 Web 端放宽与移动端集中管理之间的区别,以及 CDC 这一替代方案。

塑形,不做决策

BFF 负责呈现模型相关工作:聚合、重塑、精简负载、设置各体验的默认值,以及将上游结果转换为单个 UI 要渲染的内容。它不负责:

  • 业务规则。 定价、资格判定、工作流状态——BFF 一旦开始决定这些,就会与领域服务争夺职责,变成同时迎合两个主人的代码。SoundCloud 记录的失败模式是功能集成和业务逻辑在 BFF 中不断累积。判断边界的测试是:将列表筛选为当前用户的等级允许查看的内容,属于决策(形似授权,应由领域操作负责);对当前屏幕所需的返回结果进行截断、重排或精简,则属于塑形。拿不准时,使用下文的删除测试。
  • 产品授权。 在 BFF 调用的应用操作内部决定(bff-entry-points、六边形架构参考);BFF 只需如实传递主体信息,不做更多。
  • 持久领域状态。 BFF 可以缓存并持有每个会话或连接的状态;记录系统位于领域服务之后。

决策型逻辑应立即下沉到领域服务中,不论是否会造成重复——这套阶梯只适用于邻近呈现层的重复逻辑。当这类逻辑出现在第二个 BFF 中时,不要试图向上合并 BFF——这会造成“高度臃肿、将多种关注点挤在一起的代码”(Newman)。补救阶梯如下:首先容忍重复;只有非领域层面的基础设施逻辑才提取为共享库(并注意多个 BFF 可能因此连锁升级);到第三次出现时——也就是三次原则——再问问重复逻辑是否从一开始就属于领域逻辑;如果是,就应将其下沉到所有 BFF 之下的领域服务中。三次原则为呈现基础设施逻辑的抽象设定时机;它绝不会让业务或授权决策获得三次免费通行。

聚合、弹性、身份——简明版

  • 并行扇出;传递每个请求的截止时间,并扣除已耗费的时间;为每个上游设置基于实测数据的超时(使用延迟百分位,而不是凭猜测)。
  • 每条边只指定一个重试负责人,并为重试设置预算和随机抖动——层层重试会造成自我引发的拒绝服务。
  • 在契约中将每个响应区段分类为必需或可选;对可选区段进行降级(过期缓存 → 默认值 → 带错误的空值),而不是让整个页面失败;明确规定部分响应的结构,使其可预测。
  • 缓存时,要么将身份纳入键中,要么完全不缓存:每个请求内的记忆化始终安全;共享缓存应以租户/用户为键(或以实际使用的更粗粒度属性为键);身份变化但键中未体现的响应会导致缓存投毒泄漏。
  • 面向上游时,转发用户的身份,绝不要同时发送 BFF 的服务身份和用户标头——那会造成混淆代理问题。使用访问令牌时,应针对每个上游,将绑定会话的令牌交换为受众范围更窄、授权范围更小的令牌(若无法交换,则按资源获取令牌——参考资料中的阶梯方案)。采用事务令牌模型的部署,则应依照该模型自身的规则,原样传递受信任域范围的签名上下文。无论采用哪种方式,上游都应根据已验证的声明进行授权。

以上每一点都浓缩了参考资料中的一个章节,其中包含来源和实现机制——实施前请先阅读参考资料。

公共 API 的失效模式

当第二种使用场景开始使用某个 BFF 时,它就不再只是一个 BFF:契约会被冻结,协调成本卷土重来,而“应用程序的一部分”也会悄然变成未经版本管理的公共 API。以下是三条防护措施:

  1. 分开不同界面,并严格落实边界。 浏览器和原生应用都是公共客户端——它们携带的任何内容都无法通过密码学手段证明客户端软件的身份;即使是原生应用注册,也只能识别注册信息,而不能识别二进制文件(RFC 8252)。因此,严格来说,“验证客户端身份”并不可行;可行的是:每个 BFF 都有独立的主机/源和凭据模式——Web BFF 的浏览器请求策略和会话签发规则(bff-entry-points)可排除来自浏览器源的跨界滥用;移动 BFF 只接受其签发者、受众和授权方声明均指向其自身界面的用户持有者令牌。获得自身会话的蓄意脚本客户端,受这些策略和滥用防护措施约束,而不是被密码学手段排除——应在威胁模型中如实说明,不要夸大其效果。证明 API 和客户端遥测只能作为风险信号,绝不能当作已验证身份。
  2. 为新的使用方提供独立界面。 另一个使用场景(包括由你构建并负责的合作伙伴集成——SoundCloud 会运行专用的合作伙伴 BFF)应有自己的 BFF;对于你不控制的使用方,或非前端使用方,应提供真正的公共 API(api-design)。数据流既不是一种使用场景,也不应由 BFF 提供:应通过领域服务提供数据流——BFF 不持有可用于提供数据流的持久状态。
  3. 监控此类情况:记录每个请求所接受的凭据模式、签发者和客户端注册信息(宽事件结构见 observability);BFF 遥测中出现不熟悉的注册信息或凭据模式,属于架构事故,而不是支持问题。

纠正架构偏移(审计场景——第二个使用方已与之耦合):明确承认现状,以增量方式冻结契约,建立该使用方自己的界面(其 BFF 或公共 API),按照绞杀者模式逐路由迁移,并公布弃用日期,之后才强制执行边界拒绝策略。先强制执行会破坏你已接纳的使用方;永不强制执行,则意味着这种偏移已经成了架构本身。

反模式

  • 由第二种体验、第二个团队或任何你无法控制的使用方使用的 BFF。(同一体验的两个平台版本——iOS 和 Android 上呈现的同一应用——算作一个使用方,而非两个。)
  • 将业务规则、产品授权或记录系统放在 BFF 中。
  • 一个通用型“BFF”服务于所有客户端——这就是换了个时髦名字的“一刀切” API。
  • 为了消除重复而向上合并 BFF,而不是将共享的领域逻辑下沉。
  • 平台/后端团队拥有前端团队使用的内容。
  • 按顺序扇出调用;每一层都重试;某个可选分区的上游不可用,就导致页面返回 500。
  • BFF 使用客户端凭据调用上游,并附带 X-User-Id 标头。
  • 在已经承担 BFF 职责的元框架服务器旁边再添加一个独立的 BFF 服务。
  • 采用 N 个微服务 BFF,而不是模块化单体 BFF(按体验划分模块,部署单元只有一个);后者能以极低的运维成本服务相同的边界——每个 BFF 都是一个完整的部署单元,有自己的生命周期、安全面和 SLO。

完成检查

  • 你能说出这个 BFF 服务的唯一体验,以及负责它的唯一团队吗?
  • 第二种客户端类型会有自己的 BFF 吗?当前边界是否会拒绝不受支持的来源、凭据模式和未知客户端注册,并在威胁模型中记录脚本客户端带来的剩余风险?
  • BFF 中的每段逻辑是否都符合呈现层的需求——删除 BFF 是否不会丢失任何业务规则?
  • 聚合契约是否明确说明哪些分区可以降级?某个可选上游故障时,页面是否仍能正常显示?
  • 每条边是否恰好只有一个重试责任方,并且是否传递了截止时间?
  • 上游调用是否携带经过验证的用户身份,而非 BFF 自身的身份——使用交换后、受众范围收窄的访问令牌,或按照该模型的规则使用未更改的信任域事务令牌?
  • 是否按照 bff-entry-points 对 BFF 自身的入口进行分类并强制执行相应规则?
  • 如果你运行元框架服务器,是否已对它应用此技能和 bff-entry-points,而不是把它当作“纯前端”来处理?

评论

支持 Markdown