[微服务进阶场景实战] - 如何处理好微服务之间千丝万缕的关系

在上一章中,我们探讨了服务间频繁数据依赖的场景。除了这类需要频繁获取其他服务数据的场景,在实际开发中,我们往往还会面临另一个棘手问题——服务间的依赖关系过于杂乱。本章我们将聚焦于如何有效缓解微服务依赖复杂度的挑战。

首先,让我们把完整的业务场景梳理清楚。

1 业务场景:如何处理好微服务之间千丝万缕的关系

本节所涉及的系统主要包括商品、订单、加盟商、门店(运营)、工单(门店)等核心服务,其他辅助服务暂不展开。

系统面向两类终端用户:一是客户使用的 App,二是供公司内部员工及加盟商员工使用的运营 App。后者用户角色多样,包括总部商品管理员、总部门店运营、加盟商员工、门店工作人员等,各角色内部还可能进一步细分权限。

整体后台服务架构如下图所示

在这里插入图片描述

网关层在这里承担了几个关键职责:

  1. 路由:所有请求统一经过网关,由网关根据 URI 将请求分发至对应的后台服务,并在多节点环境下承担负载均衡。
  2. 认证:集中完成请求的身份认证与权限校验。
  3. 监控:记录全量 API 请求日志,便于 API 管理系统进行调用管理与性能监控。
  4. 限流熔断:流量过大时可在网关层限流;当后端服务响应延迟或故障时,可主动熔断,以保护后端资源并避免影响用户体验。

这个架构看起来相当标准,甚至有点“教科书”式的完美——有点像经典的 Spring Cloud 架构。但它真的能应对所有实际场景吗?我们来看两个典型的例子:

  1. 一个页面需要聚合多处数据
    例如运营 App 首页,需根据用户角色动态展示信息。若用户是门店运营人员,页面需显示:工单数量、最近工单、销售订单数据、待处理订单、库存低于安全值的商品等。这些数据分散在多个服务中。
  2. 一次操作涉及多个服务状态变更
    例如处理一个工单,可能同时需要更新库存、修改销售订单状态,并变更工单本身的状态。

于是,第一个让人头疼的问题来了:这类接口应该放在哪个服务里?

在实际设计过程中,团队经常为此反复讨论。当然,最终总能达成某个共识——比如第一个首页聚合接口,可能会放在门店服务中,形成下图所示的调用关系:

在这里插入图片描述

而第二个工单处理接口,则可能放在工单服务中,形成另一张调用图:

在这里插入图片描述

接着来看第二个问题:由于类似需求非常多,服务间频繁互相调用,时间一长,依赖关系就会变得像一团乱麻,如下图所示。

在这里插入图片描述

在这里插入图片描述

这种错综复杂的依赖会给迭代带来“地狱级”的体验——我们在之前讨论微服务痛点的文章中已详细描述过,这里不再赘述。

简单总结,当前系统面临两个核心痛点:

  1. 针对需要聚合多方数据的接口,决策其归属服务效率低下,且容易导致职责划分不一致;
  2. 服务间依赖网络复杂混乱,难以理清。

为了解决这两个问题,项目组最终决定——抽象出一个独立的 API 层

2 API层

通常,客户端(如App、网页)的接口会面临三类核心需求:

  1. 聚合:一个页面需要展示来自多个后台服务(如商品、订单、用户)的信息,这就要求后端提供一个能“打包”这些数据的接口。
  2. 分布式调用:用户的一个操作(如提交订单)可能需要按顺序或同时更新多个服务中的数据,这涉及到跨服务的业务编排。
  3. 装饰与适配:直接来自后台服务的数据格式可能不适合前端直接使用,需要剔除无用字段、转换数据结构或封装额外的状态信息。

为了系统性地解决这些问题,项目组决定在客户端与后台微服务之间,插入一个专门的 API层。此时的架构演进如下图所示:

在这里插入图片描述

这个设计带来了立竿见影的好处:

  1. 接口归属决策变得简单:遵循一条清晰的原则——凡是涉及数据聚合、业务编排或格式转换的逻辑,一律放在API层;凡是涉及核心业务数据读写(落库/查询)的逻辑,则归属到数据所在的服务。这极大地减少了团队间关于“接口该做在谁家”的争论。
  2. 服务间依赖得以简化:后台微服务之间不再为了页面渲染或复杂操作而相互直接调用,依赖关系收敛为 API层单向调用各个后台服务。后台服务得以保持职责单一和高度内聚。

架构看似更优雅了,但很快,新的挑战随着客户端的多样化而出现。

3 客户端适配问题

系统通常需要服务于多种客户端:原生App、H5页面、PC网页、微信小程序等。理想的统一调用模式如下:

在这里插入图片描述

然而,这种统一模式在实践中会遭遇如下痛点:

  1. 体验差异化需求:不同客户端的页面承载能力与交互逻辑不同。例如,App首页可能信息丰满,而小程序版本则要求极简。这迫使后台的同一个API接口需要内置复杂的逻辑,来判别客户端类型并返回差异化数据。
  2. 频繁的前端需求变动:前端迭代速度快,经常“加一个字段,减一个字段”。为了性能(遵循数据最小化原则),后端不得不频繁发布新版本来配合这些细微改动。
  3. 版本兼容复杂度激增:结合上述两点,后台服务在发布新版本时,必须同时考虑所有客户端版本的兼容性问题,维护成本和风险呈指数级上升。

为了解决这些耦合与效率问题,BFF(Backend for Frontend)模式便被引入。

4 BFF(BackendforFront)

BFF并非一种具体的架构,而是一种设计模式。其核心理念是:为不同的前端渠道量身定制专属的后端API服务。架构随之演变为:

在这里插入图片描述

不同的客户端请求经过同一个网关后会分别重定向到专门为这种客户端设计的API服务(WX API即用于微信小程序的API)。

这样做的好处非常直接:

  • 深度优化:每个BFF服务只专注于一种客户端,可以为其进行极致的数据聚合和裁剪,响应更高效。
  • 独立发布:各客户端的接口迭代可以完全独立,无需与其他渠道捆绑排期,提升了交付效率。
  • 解耦与自治:前端团队与后端团队在BFF层可以有更清晰的协作边界。

上图中的BFF架构是通用的,但还需要通过深入研究具体业务来完善。

在本项目中,业务体量极为庞大(涉及近百个微服务,数百人的研发团队),且按业务域划分为新零售、供应链、财务等多个部门。每个部门都需要面向多种客户端(公司主App、部门自己的小程序/H5)提供功能。

因此,最终的架构自然演进为 “按业务域垂直划分的BFF矩阵”,如下图所示:

在这里插入图片描述

这个架构基本上就是每个部门都会维护自己的一系列API服务。接下来展开讨论一些细节问题。

4.1 技术架构上怎么实现

整个BFF架构建立在成熟的 Spring Cloud 技术栈之上,其核心可清晰地划分为三个层次,具体架构如下图所示:

在这里插入图片描述

各层职责与技术选型如下:

  1. 网关层:采用 Spring Cloud Zuul 作为统一入口。其核心工作是服务发现(从ZooKeeper注册中心拉取可用的BFF服务实例)、路由转发,以及执行认证、限流等全局过滤器。它通过 Feign 客户端以声明式方式调用下游的BFF服务。
  2. BFF/API服务层:每个BFF服务都是一个独立的 Spring Web 应用。关键特性在于无状态不持有自己的数据库。它的核心职责是进行业务编排:聚合多个后台服务的数据、串行或并行发起分布式调用、以及对返回的数据进行装饰(字段裁剪、格式转换等)。它同样通过 Feign 调用后台微服务。
  3. 后台服务层:即传统的微服务,也是 Spring Web 应用。它们拥有独立的数据库和缓存,实现核心领域逻辑与数据持久化,是整个系统的业务能力基石。

4.2 API之间的代码重复怎么解决

在多BFF的架构下,代码重复是一个需要权衡的问题。H5与小程序通常需求差异较大,重复逻辑主要集中在PC端与App端的BFF中,因为两者页面功能常相似,仅布局不同。对此,不同部门根据自身上下文,采取了三种务实策略:

策略 具体做法 适用场景与考量
1. 公共库(JAR) 将共享逻辑封装成内部JAR包,供各BFF服务引入依赖。 适用于通用、稳定且逻辑较复杂的代码。优点是复用彻底;缺点是JAR版本升级需要所有依赖方协调发布,存在耦合。
2. 公共API服务 抽取一个独立的 CommonAPI 服务,其他BFF通过RPC调用它。 适用于可独立成服务、有明确边界的共享能力。引入了网络调用开销和新的故障点,需权衡收益。
3. 容忍重复 直接保留各BFF中的重复代码。 当重复量小、逻辑简单且变化不频繁时,维护重复代码的成本可能低于管理一个公共组件(JAR或服务)的协调与升级成本。这是一种经过评估的理性选择。

关于“透传代理”接口的特别讨论:
实践中,会存在一些BFF接口,其输入输出与后台服务的接口完全一致,仅做简单代理转发。针对这类“看似多余”的代码,项目组曾深入讨论是否要优化掉,提出了两种方案:

  • 方案A(网关直调):让网关绕过BFF,直接调用后台服务。这因严重破坏架构分层、导致职责混乱而被迅速否决。
  • 方案B(智能拦截):在BFF层设置拦截器,对未匹配的请求尝试直接转发至后台服务。此方案争论良久,最终结论是:它虽然能减少一些代码,但显著增加了系统的复杂度和不确定性(调试和问题追踪变得困难)。而编写这些“薄代理”的代码成本极低,保留它们既能维持架构清晰,维护成本也完全可控。因此,决定保留这些代理接口——这在工程上是一个“用可接受的微小冗余换取架构简洁性与可维护性”的典型决策。

4.3 后台服务与API服务的开发团队如何分工

在团队组织上,项目采用了 “垂直领域团队 + 水平BFF团队” 的矩阵式分工:

  • 专职BFF团队:设立一个集中的团队,负责所有BFF服务的开发与维护。其核心优势在于能统揽全局前端需求,作为“设计中心”确保接口划分的合理性与一致性,有效避免了后台服务边界模糊或功能重复建设的问题。
  • 领域服务团队:根据业务领域(如商品、订单、供应链)划分多个小组,专注于各自领域的核心业务逻辑与数据服务开发。

这种分工的好处是规划统一、接口规范。但一个现实挑战是:BFF层的业务逻辑相对偏重于聚合与适配,技术复杂性可能不如底层领域服务。为了保持团队技术活力和成员长期发展,项目引入了定期的岗位轮换制度,让开发人员能在BFF团队与领域团队间流动。

5 小结

关于BFF(Backend for Frontend)模式的探讨至此告一段落。需要明确的是,本章的核心目的并非推介某一种具体的技术栈或框架,而是系统性地阐述在微服务环境下,如何对接口的研发进行全局性的管理与架构设计。因此,文中涉及的内容更多地侧重于高层的设计思路、通用的决策模式以及在真实项目中必然会遭遇的典型场景。

尽管章节标题以“BFF”命名,且其作为一种专属前端的后端模式是讨论的起点,但通篇的深层逻辑实则是以BFF的理念来重构和梳理整个后端服务的分层与协作关系。可以说,BFF所代表的“为消费端定制与聚合”的思想,是贯穿整个架构演进过程的一条主线。

至此,我们关于微服务核心架构设计的系列讨论已暂告一个段落。从下一部分开始,我们将把视角转向 开发运维(DevOps)的实战领域,探讨在如此分布式、多团队的复杂系统中,如何通过工程效能工具与流程建设,来保障这一系列精良的设计能够被高效、稳定且可持续地交付与运行。论如何让开发更高效。

posted @ 2026-01-06 01:55  yihuiComeOn  阅读(200)  评论(0)    收藏  举报