9. 使用@override迁移字段
7m

概述

让我们继续构建我们的accounts!我们有一系列需要从子图迁移的字段和。但是如何安全地进行迁移呢?

在本课中,我们将

  • 了解@override
  • 了解渐进式的@override增量迁移方法
  • 开始迁移我们的accounts 类型

@override指令

为了将安全地从迁移到另一个,我们使用@override指令。

我们可以将@override应用到上,这些字段可以是类型的字段(例如QueryMutation)。这告诉,特定的现在由应用@override进行解析,而不是由另一个解析,其中的也被定义。

语义标记@override接受一个名为的参数,这个名称将是原来定义该的名称。

Query.user字段为例,目前它位于monolith子图中。我们想将它移到accounts子图。在accounts模式中,我们将在的返回类型之后添加该指示:

subgraph-accounts/schema.graphql
type Query {
user(id: ID!): User @override(from: "monolith")
}

monolith子图模式可以保持不变,但是由于请求user字段将不再调用它。

使用渐进式@override进行增量迁移

在生产环境中,我们可能希望放慢速度,在做出这些更改的同时监控性能和问题。例如,我们可能只想将所有 账号 发送 10% 或 25%。如果一切顺利且没有问题,我们可以逐步增加这个数字,直到达到完整的 100% 迁移。

为此,我们可以在 @override 中添加另一个 label。这个 接受一个以 percent 开头的字符串值,后面跟着一个括号中的数字。这个数字代表由这个 处理的 流量百分比。剩余的百分比由其他(from子图处理。

让我们再次审视一下 Query.user 。如果我们想让 账号 只处理 25% 的流量,我们可以这样注释:

schema.graphql
type Query {
user(id: ID!): User @override(from: "monolith", label: "percent(25)")
}

单点子图结构仍然相同,但现在 将将其请求的 75% 路由到单点子图,其余的 25% 路由到 账号 ,针对 Query.user

让我们进行迁移!

这是我们对 账号 的类型和 的列表。我们将逐一进行操作,前两个类型一起进行。然后,您将有机会自己完成剩余的工作!

  • 用户接口,具有 id, name, profilePicture
  • 主机类型,具有 id, name, profilePictureprofileDescription
  • 访客类型,具有 id, name, profilePicture
  • 查询.user
  • 查询.me
  • 突变.updateProfile
  • 更新配置所使用的类型:UpdateProfileInput, UpdateProfileResponseMutationResponse

用户接口

让我们从用户接口开始。我们可以在monolith/schema.graphql文件中找到其定义。复制并将其粘贴到accounts子图schema中。

subgraph-accounts/schema.graphql
"Represents an Airlock user's common properties"
interface User {
id: ID!
"The user's first and last name"
name: String!
"The user's profile photo URL"
profilePicture: String!
}

monolith子图中呢?嗯,我们仍然需要在那里定义用户接口。我们将它作为Review.author字段的返回类型来引用,我们还为HostGuest类型实现此接口。

然而,我们不需要保留所有由accounts子图负责的额外字段!我们可以删除nameprofilePicture

monolith/schema.graphql
"Represents an Airlock user's common properties"
interface User {
id: ID!
- "The user's first and last name"
- name: String!
- "The user's profile photo URL"
- profilePicture: String!
}

我们仍然需要保留字段id,因为这是识别用户特定实例的最小必要条件(就像一个占位符!)

界面定义可以在子图中默认共享,并且每个子图中的定义可能不同。这就是我们如何从单体User接口定义中删除中的nameprofilePicture字段

现在我们已经在两个中解决了User界面的问题,让我们继续处理实现类型,即HostGuest

✏️Host》实体

让我们首先看一下Host类型及其。对于每个字段,我们都添加了一个注释,说明哪个应当负责该字段,这基于关注点分离原则。

monolith/schema.graphql
type Host implements User @key(fields: "id") {
id: ID! # the key field, a unique identifier for a particular instance of a Host
name: String! # accounts subgraph
profilePicture: String! # accounts subgraph
profileDescription: String! # accounts subgraph
overallRating: Float # reviews subgraph, eventually
}

按照上述计划,我们需要做以下几件事

  • 将三个nameprofilePictureprofileDescription移动到 ,该子图将负责将这些贡献给Host
  • 单体将继续贡献 overallRating(最终,子图也将这样做,但让我们一步步来!)。

让我们开始吧!

提示: 在此过程中以及在整门课程中尝试使用并排窗口。我们将从单体子图模式中拆分类型和(在左侧),并将它们复制到accounts (右侧)。

  1. 中,让我们复制Host类型、其键以及它将负责的三个字段(nameprofilePictureprofileDescription)到accounts

    subgraph-accounts/schema.graphql
    type Host implements User @key(fields: "id") {
    id: ID!
    "The user's first and last name"
    name: String!
    "The user's profile photo URL"
    profilePicture: String!
    "The host's profile bio description, will be shown in the listing"
    profileDescription: String!
    }
  2. 让我们从name 开始并应用@override 。我们将包含from参数,并将其设置为monolith子图

    name: String! @override(from: "monolith")
  3. 我们还需要将添加到顶部的导入中。

    extend schema
    @link(
    url: "https://specs.apollo.dev/federation/v2.7"
    import: ["@key", "@shareable", "@inaccessible", "@override"]
    )
  4. 当我们保存更改时,rover dev会自动检测到更改并触发一个...哦豁!我们遇到了2个构建错误:

    error[E029]: Encountered 2 build errors while trying to build a supergraph.
    Caused by:
    INVALID_FIELD_SHARING: Non-shareable field "Host.profilePicture" is resolved from multiple subgraphs: it is resolved from subgraphs "accounts" and "monolith" and defined as non-shareable in all of them
    INVALID_FIELD_SHARING: Non-shareable field "Host.profileDescription" is resolved from multiple subgraphs: it is resolved from subgraphs "accounts" and "monolith" and defined as non-shareable in all of them
    The subgraph schemas you provided are incompatible with each other. See https://apollo.graphql.net.cn/docs/federation/errors/ for more information on resolving build errors.

    错误INVALID_FIELD_SHARING指向了两个Host.profilePictureHost.profileDescription。阅读描述可以解释错误的原因:我们在一个中重复定义了一个,并且它不是@shareable字段。在两个中都标记为@shareable可以修复错误,但是我们并不希望将其设置为可共享!我们知道这些属于accounts ,而不是monolith子图;这就是为什么要迁移的理由。

  5. 让我们完成应用@override指令到这两个

    profilePicture: String! @override(from: "monolith")
    profileDescription: String! @override(from: "monolith")
  6. 当我们保存更改并以rover dev重新合成时,我们会看到一个没有错误的成功的。太棒了,这是Host 的结尾了!

检查我们的更改

在继续之前,让我们转到本地在rover dev 运行的https://127.0.0.1:4000

  1. 让我们构建一个查询来获取特定主机的overallRating

    query User($userId: ID!) {
    user(id: $userId) {
    ... on Host {
    overallRating
    }
    }
    }

    我们将userId变量设置为user-1,这是一个主机用户的ID。

    {
    "userId": "user-1"
    }
  2. 我们可以运行并返回结果,但让我们先看一眼。记住,查询计划是解决操作查询计划。我们将以图表的形式查看它。

    https://127.0.0.1:4000

    Query plan showing only the monolith subgraph

    一个主机的overallRating字段单体subgraph,并且从中可以看出,只从这个子图获取数据。

  3. 现在让我们把name字段中去,这是我们最近迁移到accounts模块的字段。

    query User($userId: ID!) {
    user(id: $userId) {
    ... on Host {
    overallRating
    name
    }
    }
    }
  4. 再次运行,我们得到了一个新的计划!这一次,我们额外向accounts子图进行了数据获取。这意味着我们的迁移是成功的! 🎉

    https://127.0.0.1:4000

    Query plan showing both subgraphs

  5. 我们还可以将以文本形式查看,以验证name字段accounts子图

    https://127.0.0.1:4000

    Query plan showing both subgraphs

太棒了,让我们继续吧!

✏️ Guest 实体

让我们同样对Guest实体使用同样的过程。在monolith/schema.graphql文件中,我们将查看Guest类型,并为每个与子图有关的进行标注。

monolith/schema.graphql
"A guest is a type of Airlock user. They book places to stay."
type Guest implements User {
id: ID! # the key field, a unique identifier for a particular instance of a Guest
name: String! # accounts subgraph
profilePicture: String! # accounts subgraph
funds: Float! # payments subgraph, eventually
}

按照上述计划,我们需要做以下几件事

  • 将两个字段(姓名)迁移到accounts子图上,该子图负责向Guest实体贡献这些字段。
  • 单体monolith子图将继续贡献funds字段(最终,payments子图也会,但让我们一步一步来!

继续进行这些更改。在这些步骤结束时,我们的Guest实体accounts模式应该像这样:

subgraph-accounts/schema.graphql
"A guest is a type of Airlock user. They book places to stay."
type Guest implements User @key(fields: "id") {
id: ID!
"The user's first and last name"
name: String! @override(from: "monolith")
"The user's profile photo URL"
profilePicture: String! @override(from: "monolith")
}

其余的类型

让我们再次审查列表

  • User界面与id姓名profilePicture
  • Host类型有id姓名profilePictureprofileDescription
  • Guest类型有id姓名profilePicture
  • 查询.user
  • 查询.me
  • 突变.updateProfile
  • 更新配置所使用的类型:UpdateProfileInput, UpdateProfileResponseMutationResponse

现在轮到你来实现列表的其余部分了。你可以做到的!完成后,将你的代码与我们的代码进行比较。

subgraph-accounts/schema.graphql
type Query {
user(id: ID!): User @override(from: "monolith")
"Currently logged-in user"
me: User! @override(from: "monolith")
}
type Mutation {
"Updates the logged-in user's profile information"
updateProfile(updateProfileInput: UpdateProfileInput): UpdateProfileResponse!
@override(from: "monolith")
}
interface MutationResponse {
"Similar to HTTP status code, represents the status of the mutation"
code: Int!
"Indicates whether the mutation was successful"
success: Boolean!
"Human-readable message for the UI"
message: String!
}
"Fields that can be updated"
input UpdateProfileInput {
"The user's first and last name"
name: String
"The user's profile photo URL"
profilePicture: String
"The host's profile bio description, will be shown in the listing"
profileDescription: String
}
"The response after updating a profile"
type UpdateProfileResponse implements MutationResponse {
"Similar to HTTP status code, represents the status of the mutation"
code: Int! @override(from: "monolith")
"Indicates whether the mutation was successful"
success: Boolean! @override(from: "monolith")
"Human-readable message for the UI"
message: String! @override(from: "monolith")
"Updated user"
user: User @override(from: "monolith")
}

注意@override不需要应用到上的UpdateProfileInput。这是因为输入类型是一个共享类型,自动可以在之间共享。

不要忘记清理Query类型:我们不再需要那个_todo字段了。

subgraph-accounts/schema.graphql
- _todo: String @shareable @inaccessible

让我们保存我们的更改并确保在 rover dev 过程中没有错误信息;这确保了 成功!

实现渐进式覆盖

通过使用 @override 指令,我们将 HostGuest 转向由 accounts 解决。所有请求这些 都将由 accounts 处理。

在教程中,我们坚持使用基本的覆盖,但您也可以扩展下面的部分以了解如何实现渐进式覆盖的说明。

练习

以下哪项关于@override指令的描述是正确的?

关键要点

  • @override指令可以应用于fields和根类型的字段,以表示哪个应该承担解决它的责任。
  • @override指令接受一个名为from的参数,该参数指定最初定义(并正在进行覆盖)的名称。
  • 使用渐进式覆盖,我们可以提供带有额外@override指令的,即。这指定了在解决的情况下调用的百分比时间。

接下来

我们的架构看起来很棒,但我们没有任何实际的来使它真正工作!让我们处理下一个问题。

上一页

分享你对这堂课的疑问和评论

您的反馈帮助我们改进!如果您遇到困难或感到困惑,请告诉我们,我们将帮助您。所有评论都是公开的,并且必须遵守 Apollo行为准则。请注意,已解决或处理的评论可能会被删除。

您需要GitHub账号以便在下面进行评论。还没有吗? 请在我们Odyssey论坛上进行评论。