将GraphQL单体迁移到Apollo联邦的步骤
从GraphQL单体迁移到联邦超图的步骤
💡 提示
要获取完整、分步教程,请查看Voyage II: Federating the monolith。
与任何单体服务一样,随着单体API grows larger and receives contributions from more developers,团队可能会在修改和维护其GraphQL API时遇到困难。
将单体拆分为较小的 GraphQL API可能适合您的团队。借助 Apollo Federation,您可以在不牺牲依赖于统一API的客户端应用程序的情况下拆分单体。每个子图都可以独立更新、部署和扩展,同时贡献到单个统一模式。
以下是我们推荐的将单体 GraphQL API转换为联邦GraphQL API的步骤。
规划和准备
1. 在现有API前面放置一个路由器
通过制作一个“一个”的“联邦 图”来开始这个过程。您现有的单体可以在不更改任何模式的情况下作为 子图运行。
如果您尚未将架构发布到 GraphOS Studio,在您的Studio组织中创建一个新的图。选择“Supergraph”作为您的图架构。
使用以下命令将单体架构发布到 GraphOS Studio 作为一个 subgraph(根据需要修改您的
--routing-url
和--schema
路径):rover subgraph publish --name monolith \--schema ./schema.graphql \--routing-url http://monolith.prod.svc.cluster.local/graphql \--convert # necessary if you're publishing to an existing variant将 GraphOS Router 实例部署到您的环境中。
在您的环境中自托管 GraphOS Router 限于 GraphOS 企业计划。其他计划类型使用 GraphOS 的托管云路由。了解更多信息,请查看 价格页面。
设置 报头传播,以便单体可以从 router 接收任何必要的报头。
设置内部路由规则,使客户端请求转向 router 而不是您的单体。
启用使用情况指标报告到 GraphOS Studio。
将 subgraph 检查添加到您的单体的CI管道中。
到此为止,客户端请求将发送到您的新 router 而不是单体,但它提供相同的架构,因此客户端将无法察觉区别。
您不仅为联邦化架构做了准备,现在还拥有对 字段 级别的图使用情况和破坏性更改检测的可见性。
2. 识别实体
接下来,查看您架构中的所有类型,并识别可能的 实体。实体是构成您数据模型基础的类型,并且必须包括可以唯一识别其实例的 字段。
考虑这个旅行预订网站的架构
例如,如下类型User
,Reservation
,Flight
和Hotel
是唯一可识别的,而Profile
,CancellationPolicy
和Amenity
基本上是与这些实体关联的属性组。
3. 对实体进行分组
在您识别了实体后,根据它们的逻辑域或关注点对它们进行分组。这些组通常对应于产品边界,但它们也可能对应于团队边界。这将确定您将最终拥有多少子图。
账户域 | 航班域 | 酒店域 |
---|---|---|
账户 | 飞机 | 床 |
用户 | 航空公司 | 酒店 |
航班 | 预订 | |
预订 | 房间 | |
座位 |
4. 按迁移的容易程度对实体排序
在您决定如何将类型迁移到其他子图时,首先考虑以下几点:
您将同时迁移多少相关类型?
计算与实体相关联的值类型的数量。您在迁移实体时需要将所有这些类型复制到新的子图。具有较少相关标量,枚举和非实体对象类型的实体将更容易迁移。
只要您能返回一个实体引用,就无需同时移动相关实体。例如,如果您有访问Hotel
外键的权限,则可以移动Room
类型。
type Room @key(fields: "number") {number: ID!floor: Inthotel: Hotel}type Hotel @key(fields: "id", resolvable: false) {id: ID! # comes from rooms.hotel_id in the database}
可能更安全、更容易地移动整个Room
类型,但只移动Hotel
类型的“骨架”。查询计划器可以从中查询其余的Hotel
字段,直到您移动该类型。
迁移过程中您的查询计划将变得多复杂?
如果你从移动与其他类型深度互联的类型开始,可能会为你的's查询计划引入不必要的复杂性。例如,考虑以下查询:
query MyFlights {me {reservations {flights {...FlightDetails}}}}
此查询返回属于特定User
的对象的Reservation
列表,每个Reservation
都包含一系列Flight
。如果你开始移动Reservation
类型到另一个subgraph,此查询将产生一个"A→B→A"的查询计划(依次获取User
、Reservation
以及Flight
,三个并行的subgraph获取):
在这个阶段,一个更好的选择可能是移动Flight
类型,这样查询计划将更加高效,在获取User
和Reservation
之后再获取Flight
:
当你将一个类型移动到另一个subgraph时,你还应该移动返回该类型的所有根级别字段(例如Query.flight(id:)
。这样,该类型的对象可以随着单个subgraph操作返回。在最佳情况下,查询计划可以只需更少的子图操作就获取任何额外的数据:
在迁移类型之间的过程中,某些查询计划不可避免地变得更加复杂。通过对你的实体进行排名并将影响最小的那些首先移动,你可以最小化这种增加。
实现
1. 将你的单体做为一个真正的subgraph
现在你有了一个迁移计划,你可以开始进行架构和代码变更。第一个变更是将Apollo Federation subgraph规范添加到单体中。涉及步骤取决于你与单体语言和框架一起使用的Apollo-Federation兼容库。
需要添加的最重要功能是定义你的实体(通过添加@key
指令)并添加它们的引用解析器。
2.部署你的新subgraph
从空的subgraph开始,快速设置部署和持续集成管道。你可以使用这个存根subgraph规范,它不会影响面向客户的架构:
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable", "@inaccessible"])type Query {_todo: String @shareable @inaccessible}
在您的新的subgraph部署之后,设置模式检查和发布,以便您可以快速捕获组合错误,并开始为supergraph做出贡献。
3. 将实体及其相关类型和相关根字段移动到subgraph中
- 首先,在单体中标记所有您打算移动到subgraph中的值类型(枚举和非实体对象类型),标记为
@shareable
。 - 将类型和字段复制到subgraph模式中,并从单体导入它们的解析器。
- 部署subgraph并直接调用它进行测试。使用
_entities
根字段以测试实体之间的连接。
当您对新的subgraph的行为和性能满意时,您可以开始将所有流量移动到它,并清理单体。
- 使用
@override
指令来标记subgraph中的字段,用@override(from: "monolith")
标识,告诉查询规划器优先考虑新的subgraph而不是单体。 - 从单体模式中删除类型和字段。
- 从单体中删除不需要的解析器。
- 从subgraph中移除
@override
指令。 - 在适当的时候,从subgraph中的类型和字段移除
@shareable
。
4. 根据需要将其他功能迁移出单体
在迁移的后期,您可以决定是否保留单体以处理位于您域中间的少数实体(例如User
),或者将单体完全解散成新服务并使其退役。无论如何,您的全新联邦化的GraphQL API的未来扩展都处于良好的位置。