一个中型到大型企业通常有几十个微服务,每个服务有自己的数据库和数据域。前端应用需要的数据往往跨越多个服务的边界——一个用户详情页需要从用户服务、订单服务、支付服务、通知服务获取数据。
在REST架构中,这通常意味着前端需要调用多个API端点并自己组装数据,或者在后端引入一个BFF(Backend for Frontend)层来做聚合。
GraphQL Federation提供了第三种选择:每个微服务继续做自己的事,通过联邦组成一个统一的数据图,让客户端用一次查询获取所有需要的数据。
子图(Subgraph):每个微服务定义自己的GraphQL Schema,只包含自己拥有的类型和字段。
实体(Entity):跨服务共享的数据单元,用 @key 标识。实体在哪个服务定义基础字段,其他服务可以 extend 这个实体来添加自己管理的字段。
网关(Gateway):接收客户端查询,根据Supergraph的查询计划分解到各子图,组装结果。
# 用户子图 — 定义User实体 type User @key(fields: "id") { id: ID! name: String! email: String! } # 订单子图 — 扩展User实体,添加订单相关字段 extend type User @key(fields: "id") { orders(status: OrderStatus): [Order!]! } type Order @key(fields: "id") { id: ID! status: OrderStatus! total: Float! userId: ID! } # 支付子图 — 扩展Order实体 extend type Order @key(fields: "id") { paymentMethod: String lastPaymentAt: DateTime }

构建Supergraph的流程:
# 1. 每个子图团队用Rover CLI注册自己的Schema rover subgraph publish my-app@prod \ --name users \ --schema ./users/schema.graphql \ --routing-url http://users-service:4001/graphql rover subgraph publish my-app@prod \ --name products \ --schema ./products/schema.graphql \ --routing-url http://products-service:4002/graphql # 2. 网关从注册中心拉取Supergraph # 3. 客户端查询到达网关,查询计划器分解查询到各子图
Apollo Rover CLI是联邦管理的核心工具。它处理Schema注册、Supergraph构建、兼容性检查等工作。
当一个涉及多个子图的查询到达网关:
{ user(id: "1001") { name # → 用户子图 orders { # → 订单子图 total # → 订单子图 items { # → 订单子图 product { # → 商品子图 name price } } } notifications { # → 通知子图 message read } } }
网关的查询计划器分析查询,生成执行计划:
name)这个执行计划考虑了数据依赖和并行性,在保证正确性的前提下最大化并发执行。
当订单子图需要返回 items.product 时,它自己没有产品数据。网关发现 Product 是商品子图定义的实体,于是把 product.id 列表发送给商品子图的 __resolveReference 解析器,获取完整的产品数据。
这个过程叫做查询分解与实体解析(Query Decomposition and Entity Resolution),是联邦的核心机制。
构建企业级数据图的关键在于:每个子图只管理自己负责的数据域,不试图做别人的事。这种边界清晰度是联邦架构的可维护性的基础。
@key 是联邦的"外键",它的设计直接决定查询计划的效率。单一 id 是最简单的键,但业务实体常常需要复合键:
# 多租户场景:同一 id 在不同租户下是不同实体 type Order @key(fields: "tenantId id") { tenantId: ID! id: ID! total: Float! }
复合键要求每个子图都返回完整的键字段,否则网关无法对齐实体。另一个常见设计是把业务键(订单号、sku)与内部主键分开:内部主键做 @key 保证稳定,业务键暴露为普通字段。键字段一旦定下来就很难改,因为它被所有子图的实体解析器引用,改动等于一次跨服务重构——所以实体键要在联邦建设初期就认真设计。
联邦最常见的治理问题:两个子图都声明了同一个实体的同一个字段。网关无法决定听谁的,会在组合时报错。解决方式有三种:
实践上,团队容易掉进"谁都能改 User"的坑。建议在组织层面约定:每个实体字段只有一个 owner,owner 之外的子图想加字段,要么 extend 出新字段(不冲突),要么通过 @requires 计算派生字段(依赖已有字段)。这条约定比任何工具都能减少联邦冲突。
联邦引入网关后,故障面也增加了:
理解了这些故障模式,才能为联邦架构设计出对应的监控与告警,而不是等服务出问题再救火。
