エンジニアリンググループの冨岡 (@jooohn) です。出張でNYにきています。NYへの出張は二度目なのですが、同僚のChris (彼はUK, JP, USと三カ国のM3を渡り歩いています!) とWashington, D.C.にいくなどして休日も満喫しています。
現在はM3 USAが運営するニュースサイトMDLinxのリニューアルプロジェクトに関わっています。そこで利用しているGraphQL (Apollo) の活用事例を紹介します。
新しいMDLinx の構成
新しいMDLinxでは上図のように、k8sクラスタ内にいくつかのサービスが存在するマイクロサービス構成になっています。各サービスではGraphQLを共通のインターフェースとして利用しており、webhook用のエンドポイントなどの特殊な場合を除きGraphQLのAPIを呼びます。
MDLinxの編集部はContentfulを利用して記事を作成します。この際、一部Contentfulのネイティブ機能では足りない部分(古い記事のアーカイビングなど)はUI Extensions機能を利用して編集部にContentfulから離れることなく機能を提供しています。このUI Extensionsが Content Serviceを利用する場合には、Content ServiceのGraphQLのAPIを利用します。
MDLinxの編集部が触るサービスは1つなので問題になりませんが、一般ユーザー側は各サービスの機能を広く利用します。この際各microservicesのエンドポイントをバラバラに叩くのではなく、Gatewayサーバーが他のサービスのGraphQL schemaを1つにまとめて、フロントエンドからは1つのschemaに見えるようにまとめています。
図にあるLegacy MDLinx Serverのように、すぐには移行できない機能もGraphQL schemaの一部として提供できています。フロントエンドはGatewayが提供する唯一のGraphQLスキーマだけを知っていればいいことになり、古いAPIを気にする必要はありません。GraphQLのインターフェースによってバックエンドが隠蔽されるため、サービスの分割・統合・移行などもやりやすくなりますね!
この機能を手軽に実現できるようにしているのが、Apolloが提供するApollo Federation (Schema stitching) です。
Schema stitching (deprecated)
Schema stitching (deprecated) - GraphQL Tools - Apollo GraphQL Docs
Apollo Federationの説明をする前に、Schema stitchingという機能を紹介します。
Schema stitchingはまさに上記で説明したような、「複数のスキーマを統合して1つのスキーマとして提供する」という機能です。統合する際にスキーマをある程度コントロールすることができ、query名にprefixをつける、一部のqueryを無視するといったことができます。今回の私達のユースケースでは、Content ServiceのGraphQL schemaは内部(MDLinx編集部)向けのものも含まれており、それをフィルタリングして一部の一般ユーザー向け機能のみ公開するといったことができます。
便利な機能ですが、5月にdeprecatedとなってしまい、代替として次に紹介するApollo Federationが提供されました。
Apollo Federation
Introduction - Apollo Server - Apollo GraphQL Docs
Apollo Federationは、いくつかのマイクロサービスのユースケースにフィットするよう作られたSchema stitchingの後継のような機能です。
Schema stitchingでは、いくつかのサービスを1つのschemaにまとめるマージ機能を主目的としている印象がありました。一方Apollo Federationは各サービス間の連携をより強力にサポートしています。
# reviews service type Review { body: String author: User @provides(fields: "username") product: Product } extend type User @key(fields: "id") { id: ID! @external reviews: [Review] } extend type Product @key(fields: "upc") { upc: String! @external reviews: [Review] }
上記はhttps://www.apollographql.com/docs/apollo-server/federation/introduction/#a-first-look の例ですが、User
,Product
は他サービスのSchemaから提供されているものです。Reviewsサービスは、他サービスで提供されているUser
,Product
を利用してReview
型を提供している他、User
、Product
にそれぞれreviews
というフィールドを拡張しています。
このように、microservicesの各サービス間での依存関係を上手く表現できます。
ApolloはJSのプロダクトですが、他の言語で実装されていてもFederation specification - Apollo Server - Apollo GraphQL Docsに従うことによってFederationサーバの一部として機能できます。これもGraphQLによってインターフェースと実装を分離された恩恵ですね!(なおMDLinxのサーバーサイド全てNode.js, TypeScriptなので素直にApollo Serverを使っています。)
一方でschemaの統合機能はかなりおおざっぱになってしまいました。
queryを非公開にする機能がなくなっているなど、Schema stitchingでできたことができなくなっています。 https://github.com/apollographql/apollo-server/issues/2812
現在MDLinxではこの制約のためSchema stitchingを一旦利用し続けていますが、今後サポートする予定だそうなので楽しみです!
まとめ
MDLinxでのGraphQL (Apollo) の活用方法として、複数microservicesのschemaを1つのGraphQL schemaとして提供する事例を紹介しました。
GraphQLはAPIリクエストを効率的にまとめる・絞ることも重要な役割の1つですが、インターフェースと実装を分離することによってサーバーサイドの実装に選択肢も与えてくれています。GraphQLがあくまでインターフェースとして存在していることは、私がGraphQLを好きなポイントの1つです。
We are hiring!
エムスリーでは、NYのバーガーが好きな人や、GraphQLでインターフェースを分離したい人を募集しています!