こんにちは、マネーフォワード 福岡開発拠点のStart up Studio部で新規プロダクトを開発しているバックエンドエンジニアのダンと申します。
今回の記事は私たちのチームでマイクロサービスアーキテクチャにあるAPI Gatewayの課題を解決できるソリューションを検討の経緯、そして辿り着けた優れたGraphQL Federationについて紹介させていただきます。
- 課題
- GraphQL Federationの前の世界
- GraphQLの誕生
- GraphQL Federation登場 - "One endpoint to rule them all"
1. 課題
まずは、マイクロサービスアーキテクチャによく出会うパターンの例を説明します。
製品をレビューするシステムを構築するプロジェクトで、フロントエンドとバックエンド二つのコンポーネントがある。 バックエンド側はドメインによる三つのサービスに分けて、それぞれのサービスは以下の情報を管理する:
- Users Service: ユーザー情報を管理するサービス
- Products Service: 製品情報を管理するサービス
- Reviews Service: 製品に対するレビューを管理するサービス
で、フロントエンド側(モバイルやウェブブラウザやサードパーティアプリなど)は以下のユーザーのニーズに対してサービスを提供する
- ユーザーは製品のリストを見たい!
- ユーザーはその製品のレビューを見たい!
このニーズを満たすため、フロントエンドはバックエンドのサービス群とやりとりする。
- どうやってやりとりを行うのか?
- そのやりとりにおいてなんの問題があるのか?
- そして、その問題をどのように解決するのか?
。。。 というたくさん疑問が出てきて、チームはこの課題について相談・調査を行いました。
これから、上の課題を解決するため、チームで取り上げた案、そして各案のメリデメについて説明していく。最後に辿り着けた優れたGraphQL Federation案について紹介させていただきます。
2. GraphQL Federationの前の世界
デベロッパーとして問題解決する際、まず課題に対する仮説を出し、その仮説の問題をきちんと理解したうえでよりよい案を探すのがよいです。これらの仮説検証、実施、カイゼンのサイクルを繰り返すことでよりよいプロダクト開発ができます。PDCAを回す一環としてAPI Gatewayの問題を解決していきましょう!
2.1. 案1: ゴリゴリ接続してやり取りする
図の通り、各フロントエンドは各サービスに直接接続してやり取りする。
上の設計においてどんな問題があるのか?
案1の問題 | 説明 | どうしたらいいのか | |
---|---|---|---|
1 | フロントエンドは全てのサービスを認識しないといけない | 各フロントエンドクライアントは以下の全ての情報を調べないといけない: -どのサービスがあるのか? - 各サービスはどんなAPIを提供ているか? - 各サービスのホストはなんでしょうか? - 新しく登場したサービスはなんでしょうか? 。。。 |
APIは1つのサービスとだけ連携したい! |
2 | 各サービスは共通の懸念があり、その懸念を解決するため何回も同じな実装をしないといけない | 共通の懸念は認証認可やセキュリティなどを想定していて、各サービスに同じな仕組みを繰り返して実装したら、この後メンテナンス・バグ対応は大きいコストになってしまう | 各サービスに同じ実装、コンフィグを何回も繰り返したくない。 できるだけサービス自体のドメインの問題だけを集中して開発する |
3 | 全てのサービスはインターネットに公開されてしまう | 公開されるサービスが多ければ多くほど攻撃リスクが高まる。そしてメンテナンス・運用のコストも大きくなる | インターネット最前線にできるだけ少数のサービスだけ出す |
→ 明らかにこの案にはたくさんの問題があって、マイクロサービスのスケーラビリティの利点は失ってなった。
次に、上の問題を解決できる案2を見ていきましょう。
2.2. 案2: API Gateway
案1にはフロントエンドとバックエンドのやりとりは分散されて、管理しにくくなった。その問題を解決ため、フロントエンドとバックエンドの全てのリクエストを一つのサービスで集結して処理する。その目的でよく採用されているのはAPI Gatewayパターンである。
で、API Gatewayにより案1の問題がどのように解決できたのか?
案1の問題 | API Gatewayにより解決 | |
---|---|---|
1 | フロントエンドは全てのサービスを認識しないといけない | フロントエンドはAPI Gatewayだけ通信する。API Gatewayのことだけ分かればサービス群とやりとりできる |
2 | 各サービスに共通のコンサーンを何回も実装しないといけない | サービス群の共通の仕組みはAPI Gatewayに任せる |
3 | 全てのサービスはインターネットに公開されてしまう | インターネットに公開されるのはAPI Gatewayだけです。裏側のサービス群は隠される |
いろいろなAPI Gatewayソリューションがあって、オープンソースプロダクトもGatewayを内製化した会社もあるが、この記事でオープンソースで流行っているKong API Gatewayの仕組みを簡単に説明していく。
上の図を見てKongの仕組みを深掘りしていきましょう。 簡単に言うとKongの責任は(a)フロントエンドからのリクエストを受けて、(b)そのリクエストを適切なサービスにフォワードして、(c)そのサービスのレスポンスを発信元のフロントエンドアプリに返すことです。
- (b)でどのように実現できたのか?
- API GatewayにリクエストのURLのパターンと各サービスのホスト・ポートのマッピングをコンフィグとして保存する(図のrouting configurationテーブル)
- 例えば:
/user/
で始めるパスのリクエストを受け取ると、コンフィグの通り、user serviceに渡す
- 例えば:
- マッピングのルールによってリクエストは正しく送信先にフォワードされる
- 新しいサービスができたら、マッピングコンフィグに新しいルールを追加するだけで良い。
- API GatewayにリクエストのURLのパターンと各サービスのホスト・ポートのマッピングをコンフィグとして保存する(図のrouting configurationテーブル)
-> サービス群は増えれば増えるほど、フロントエンドはそういうことを気にせずにAPI Gatewayに聞きに行ってヨシ!この案2で自信を持ってガンガンシステムを構築して行く!
ですが、システムがどんどん進化して、ワクワクながら残念なのだが、たくさんのニーズが出てきて、上の仕組みを導入しつつ新しい問題が現れる。それは:
案2の問題 | 説明 | どうしたらいいのか | |
---|---|---|---|
1 | 複数のサービスを跨いでデータを取得するリクエストはどうするのか? | 上のKongの仕組みの説明の通り、クライアントはKongにリクエストを投げて、Kongが適切なサービスにフォワードしてもらうが -> クライアントで最終的な結果を取得するため複数リクエストを送らないといけない→フロントエンドでの実装がどんどん複雑になる |
フロントエンドは一つのリクエストだけで欲しいデータを手に入れるようになったら良いな! |
2 | Single point of failureの危機・SPOF | API Gatewayで集結して全てリクエストを処理できるが、API Gatewayが壊れたら全てのバックエンドサービスに影響する | できるだけAPI Gatewayの責任を小さくしてデバッグしやすくしないと |
2.3. 案3: Backend for frontend - BFF
まずは、KongのようなAPI Gateway(案2)の問題1(複数のサービスを跨いでデータを取得するリクエストはどうするのか?)から見ていきましょう。
- 適切なサービスにルーティングをサポートするという案2のようなAPI Gatewayだけではなくて、フロントエンドが一つのリクエストで複数サービスのデータを取得するため、API Gatewayにも必要なビジネスロジックを実装する。そういうものはBackend For Frontendである。
- フロントエンドのニーズにより、どの複雑のリクエストでも、BFFに各サービスへのリクエストして、それぞれのリクエストの結果をハンドリングして、合わせてフロントエンドに最終的な結果を返すという実装できる。
→ 複数のサービスを跨いでデータを取得する問題が解決できて良さそう!
ただし。。。上の仕組みこそにより新しい問題が出てきた。
この記事の例には三つのサービスしかないけど、プロダクトがどんどん進化してたくさんのサービスが出てくることを想定する。サービス群が増えれば増えるほどBFFにの実装(上記の図の赤いボックスのところ)は複雑になっていく→BFF自体は大きくなって新しいMonolithサービスになってしまう。
案3の問題 | 説明 | どうしたらいいのか | |
---|---|---|---|
1 | BFFが段々複雑になってメンテナンスは辛くなる | サービス群がどんどん増えていて、BFFに追加実装もしないといけなくて複雑になってしまう。 その実装は再利用できなくて新しいビジネスロジックやバックエンドサービスなどが出てきたらBFFに追加しないといけなくて、つまり、ビジネスロジックと密結合である |
ビジネスロジックは各サービス自体で持たせて、API Gatewayのようなコンポーネントはできるだけ変更を下げて、スケーラビリティできる |
2 | Single Point Of Failureの危機も上がる | BFFでただフロントエンドのリクエストをサービスに渡すことだけではなくて、システムのビジネスロジックも含めたのでBFFの責任は大きすぎてデバッグしにくくてエラーが発生しやすい | 上のように、API GatewayのようなコンポーネントはAPI Gatewayの責任を持てば運用しやすくなってダウン危機が下がる |
→ 「ドメインによりサービスを分けて各チームは並行で働けるようになる」というマイクロサービスの利点はBFF仕組みでなくなった。 で、API Gatewayの良さも維持しつつ、責任を小さいままで、複雑なニーズも対応できるサービスがあったら最高ですね!それはGraphQL Federationができることだよ!
3. GraphQLの誕生
API Gatewayの話は一旦置いといて、まずは、近年話題になるGraphQL APIについて復習していきましょう!
GraphQLを耳にしたこと、あるいは触ったことがある方は案2の問題1で言及した「フロントエンドは一つのリクエストだけで欲しいデータを手に入れるようになったら良いな!」ということに慣れているかもしれない。それはGraphQLの利点の一つだ。
GraphQLの公式なサイトによると「GraphQL(グラフQL)とはAPIむけにクエリ言語、および定義した型システムを利用したクエリを実行するためサーバーサイドランタイムである」。
GraphQLはたくさんの利点があるが、ここで、フロントエンドの観点からRESTとGraphQLの幾つかの違いところを注目したい:
- REST APIを提供するサービスはエンドポイント(URL)のリストがあって、そのURLリストでサービスのリソースを扱える。フロントエンドは一つのリクエストに一つのURL、かつ一つのHTTPメソッド(いずれかのgetやpostやdeleteなど)しか利用できないため、リクエストができることはそのURLとHTTPメソッドができることに限られる。
- 一方、上記の図の通り、RESTのようなURLのリスト代わりに、GraphQL APIを提供するサービスは一つURL(
/graphql
)かつそのURLにリクエストするのがHTTP postメソッドだけがある。サービスのリソースは記述的なGraphQL Schemaで定義される(上記の図の赤いボックス)。そのSchemaのを基づいて、フロントエンドは必要なリソースをクエリに求めれば相当のレスポンスを手に入れる。- それで、RESTよりGraphQLの方は柔軟性がある。
- 例えば:上のイメージのUser Schemaにより、リクエストボディに好きなリソースをクエリで書ければ一つのリクエストで全部取得できる!フロントエンドはリクエストでuserのidだけ取りたかったらidだけもらって、そしてuserのid, name, addressesを一つリクエストで手に入れる。クエリ自体は読みやすくて、データ構造もわかりやすくて、フロントエンドにとってデータを扱いやすいね。
RESTと比べてGraphQLも幾つかデメリットがある:
- GraphQLレスポンスのステータスコードはいつも200であり、失敗する場合、レスポンスのトップレベルのキーはerrorsであって、そのキーの値を見ないといけない。これによりエラーハンドリングとモニタリングの仕組みを変わらないといけない。
- GraphQLには一つのURLとPOSTメソッドだけ使われるので、HTTP Cachingでリソースを再取得が利用できない。ですが、それ代わりに、GraphQL自体はglobally unique IDsのキャッシュ仕組みがある。
4. 案4: GraphQL Federationの登場 - "One endpoint to rule them all"
上記に説明したGraphQLは一つのサービスだけでした。 GraphQLはやはりたくさんの利点がある:
- 欲しいデータをクエリとしてリクエストして、そのデータこそを取得できる。
- APIのSchemaは自体は記述的でサービスは何を提供するかわかりやすい。
- 一つのリクエストで欲しいデータ全てもらう!
こういう利点は複数のサービスでもフロントエンドに提供できたらものすごく良いことだね!
それはGraphQL Federationができることだよ。
GraphQL Federationは2019年にApolloGraphQL会社により作られた。今までFederationを採用された一番大きいシステムはNetflix社のものかもしれない。 GraphQL Federationの裏側の基盤の理論はこちら参考してくだいさい:https://principledgraphql.com/
一言でいうと、GraphQL Federationはバックエンド側で各サービスの自分のドメインに集中して並行で開発できて、自動的に一つのサービスにアグリゲートされてフロントエンドに一つのGraphQL APIとして提供する。
GraphQL FederationによりここまでAPI GatewayとBFFの何の問題を解決できるのでしょうか?
問題 | GraphQL Federationにより解決 | |
---|---|---|
1 | 複数のサービスを跨いでデータを取得するリクエストはどうするのか? | フロントエンドは一つのGraphQL APIだけ取り扱って、欲しいデータをクエリに定義して一つのリクエストで取得できる |
2 | Single point of failureの危機・SPOF | BFFのような重くて複雑の実装が要らなくて、各サービスはFederation仕組みに沿って実装するだけで、GraphQL FederationのAPI Gatewayはとてもライトで、運用とバグトラッキングしやすい |
上記の通り、GraphQL Federationを導入するため、各サービスもフロントエンドにGraphQLでAPIを提供しないといけない。既存のサービスはRESTであればGraphQLに変換することが必要だが、その変換によりGraphQLとGraphQL Federationの圧倒的な利点を得る:
- 各チーム、サービスは並行で開発を進められて、より早くスピードでより良いプロダクトを開発できる
- フロントエンドはサービス群を認識せずにFederationのAPI GatewayのAPIを一環で活用できる
具体的に:
- GraphQL Federationは何が優れたのか?
- どのようにそういうことを実現できるのか?
- どのように導入するのか?
ここまで長くなったので、次の記事でGraphQL Federationについて詳しく説明しておきます! このAPI Gatewayの進化の記事を振り返りながら、お待ちください。
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【サイトのご案内】 ■マネーフォワード採用サイト ■Wantedly ■京都開発拠点
【プロダクトのご紹介】 ■お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android
■ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』
■だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』