Movatterモバイル変換


[0]ホーム

URL:


Upgrade to Pro — share decks privately, control downloads, hide ads and more …
Speaker DeckSpeaker Deck
Speaker Deck

GraphQL「良さ」・「難しさ」再探訪 〜スタディサプリにおける実例〜 / StudySap...

Avatar for Recruit Recruit
February 08, 2024

GraphQL「良さ」・「難しさ」再探訪 〜スタディサプリにおける実例〜 / StudySapuri with GraphQL

2024/02/08 に「LayerX、スタディサプリ、SHEと考える GraphQLが向いている現場とは?運用実践LT」で、内山高広( @highwide )が発表した資料です。 #Offers_GraphQL実践LT

Avatar for Recruit

Recruit

February 08, 2024
Tweet

More Decks by Recruit

See All by Recruit

Other Decks in Programming

See All in Programming

Featured

See All Featured

Transcript

  1. #Offers_GraphQL実践LT GraphQL 「良さ」・「難しさ」 再探訪 〜スタディサプリにおける実例〜 内山高広 @highwide 2024.02.08 #Offers_GraphQL実践LT

  2. #Offers_GraphQL実践LT Agenda | 00 01 02 03 04 About Me

    & Us GraphQL 「良さ」 再探訪 GraphQL 「難しさ」 再探訪 「良さ」「難しさ」を踏まえたスタディサプリでの実際 得られたインサイト 2
  3. #Offers_GraphQL実践LT About Me & Us 00 3

  4. #Offers_GraphQL実践LT こんにちは! • 内山 高広 / @highwide • スタディサプリでプロダクト基盤のWeb開発を行っています •

    GraphQLを使っているチーム→使っているチーム→使っていないチー ム...と、スタディサプリの複数チームに携わってきました ◦ Ruby on Rails/node.js/Go/Reactなどを書いてきました • 長男(4歳)と次男(0歳)の子育てを楽しんでいます 4
  5. #Offers_GraphQL実践LT 今日話す「スタディサプリ」について 5 スタディサプリブランドには様々 なサービスがありますが、今日は 学生の方に主に使っていただいて いるサービスについて話します。 https://studysapuri.jp/

  6. #Offers_GraphQL実践LT GraphQL 「良さ」 再探訪 GraphQLを導入するメリットなんて、いろんなところで語られてますが改めて... 01 6

  7. #Offers_GraphQL実践LT 出発地点: RESTful(あるいはRESTish)なアーキテク チャとの比較から • ここではURLで表現される「リソース」をHTTPメソッドで操作するステー トレスなAPI設計をイメージします ◦ ex: ▪

    GET /articles => 記事一覧 ▪ GET /articles/{id} => 記事詳細 ▪ POST /articles => 記事作成 7
  8. #Offers_GraphQL実践LT sample: 記事一覧画面 8 Awesome Media Article 1 summary Article

    2 summary Article 3 summary GET /articles
  9. #Offers_GraphQL実践LT sample: 記事一覧画面 9 Awesome Media Article 1 summary Article

    2 summary Article 3 summary GET /articles …みなさんが作ってるシステム、 本当にこんなシンプルなやつですか?
  10. #Offers_GraphQL実践LT たとえば、こういう感じだったりしませんか? 10 Awesome Media Article 1 summary / author

    Articleに似せた記事広告 (システム的には別Entityとして表現されてる) 求人 情報 ユーザー名/前回ログイン日時 記事カテゴリ一覧(ユーザーの好みでパーソナライズされてる) 記事閲覧数 ランキング 注目の Author 天気 予報 写真 いろいろ 動画 いろいろ Article 2 これは、 「GET /articles」 ...で、いいのか?
  11. #Offers_GraphQL実践LT 複数リソースで成り立つ画面/APIとの向き合い方あれこれ 11 • 複数のリソースの中から代表的なリソースに着目する ◦ 「複数リソースを取得する必要はあるが、あくまでこれは"記事一覧"だ」 • 複数のリソースによって構成される1つの(メタ)リソースを見出す ◦

    「これはDashboardというリソースだ」 • リソース指向ではなく、画面指向やコンポーネント指向な命名をする ◦ 「内部的なAPIルーティングも /api/toppage にしよう」 • 単一リソースを返すシンプルなAPIをクライアントが必要に応じて複数呼ぶ ◦ 「/articles と /authors と /videos と...を非同期で呼ぶ」 ◦ ※ この発表では扱いきれないのですが、React Server Componentとの相性◎
  12. #Offers_GraphQL実践LT その他、RESTfulな設計で直面する難しさ 12 • 同一リソースを扱うがコンテキストによって微妙に異なる表示項目 ◦ 導線や利用端末によって記事詳細ページで表示している情報が異なる仕様だが、 すべて「GET /articles/{id}」で取得している ▪

    本来は不要なデータもまとめて取得してしまう "over-fetching" が起こっ ている • リソース指向よりもユースケース指向で表現したくなるような更新系API ◦ 「accountのactivationを行いたいが、"activationのPOST"として表現する よりも"accountのactivate"として表現したくなる」
  13. #Offers_GraphQL実践LT これらの課題に対しての、GraphQLという解決策 13 クライアント サーバー GraphQL スキーマ Query: 必要なエンティティの必要なフィールドを 1つのリクエストですべてクエリする

    Mutation: 更新処理に必要なパラメータを渡し、必要 な返り値をクエリする (多くの場合) 単一のAPIエンドポイントで QueryやMutationを待ち受ける ex: /api/graphql
  14. #Offers_GraphQL実践LT これらの課題に対しての、GraphQLという解決策 14 クライアント サーバー GraphQL スキーマ Query: 必要なエンティティの必要なフィールドを 1つのリクエストですべてクエリする

    Mutation: 更新処理に必要なパラメータを渡し、必要 な返り値をクエリする (多くの場合) 単一のAPIエンドポイントで QueryやMutationを待ち受ける ex: /api/graphql over-fetchingの心配もなく、 一画面の表示にに大量のリクエ ストを発行する必要もなくなる リソース指向の APIエンドポイント設計は不要に
  15. #Offers_GraphQL実践LT 「何を返す必要があるのか」の知識をクライアントに寄せられる 15 Awesome Media Article 1 summary / author

    Articleに似せた記事広告 (システム的には別Entityとして表現されてる) 求人 情報 ユーザー名/前回ログイン日時 記事カテゴリ一覧(ユーザーの好みでパーソナライズされてる) 記事閲覧数 ランキング 注目の Author 天気 予報 写真 いろいろ 動画 いろいろ Article 2 ※ 仮に画面で表示したい項目が変 わっても、それが既にスキーマで定義 されたものならば、サーバサイドの開 発は不要 query TopPageQuery { article { title summary } adArticles { title summary } job { (以下略)
  16. #Offers_GraphQL実践LT スキーマ駆動開発によるフロー効率の向上 16 クライアント サーバー GraphQL スキーマ 型の提供: スキーマが決まれば、型への変換ができるの で、サーバーの実装を待たず開発着手可能

    resolverの実装: スキーマで定義されたエンティティや フィールドを実際に返せるような実装 ※ もちろん、クライアント-サーバー間のコントラクトとなるスキーマさえあればスキーマ駆 動開発はできるが、GraphQLスキーマのちょうど良い表現力や、クライアントの型に変換 するエコシステムの充実度合いは、開発体験をより良いものにしている (と、思う。最近だとTypeSpecのことはちょっと気になっている)
  17. #Offers_GraphQL実践LT 02 17 GraphQL 「難しさ」 再探訪 「良さ」を理解することで、それに立脚した「難しさ」の解像度が上がるはず

  18. #Offers_GraphQL実践LT 単一のエンドポイントに様々なユースケースのリクエストを 行うことによる、これまでの慣習の見直し 18 • Observability(観測性) ◦ SLI/SLOをHTTPエンドポイントごとに計測しているシステムは少なくないはず ◦ すべてのQueryやMutationを受け付ける

    /api/graphql では、そのSuccess Rateを見ても、特定ユースケースの兆候はわからない • Authorization(認可) ◦ たとえばRailsのようなMVCフレームワークの場合、ルーティングに対応する個々 のControllerで認可を行うことが多く、そのためのライブラリも充実している ◦ すべてをgraphql_controllerでハンドリングすることになったとき、どのように 認可を行うべきか
  19. #Offers_GraphQL実践LT 1つのリクエストで複数のエラーが起こりうることによるエ ラーハンドリングの難しさ 19 • 1つのリクエストで複数のエンティティの取得を行えるということは「Aの取得 には成功した」「Bの取得には失敗した」という部分成功/失敗が起こりうる • この部分成功/失敗を鑑みたうえで、以下のような選択を迫られる ◦

    (これまで通り)HTTP status codeでエラーを表現する ◦ response bodyのerrorsというfieldにエラー情報を詰め込む ◦ エラーを示すエンティティをスキーマ上で定義する
  20. #Offers_GraphQL実践LT N+1はRESTでも起こるが、なぜとりわけ 「GraphQLでは起こりやすい」と言われるのか 20 type Article { title: String! }

    スキーマ定義 「スキーマで定義したfiledをどのように 返すか」を実装するresolver Article: { title: () => { // ここでDBから取得したArticle 1レコードが持つ // titleカラムのデータを返す処理 } }
  21. #Offers_GraphQL実践LT N+1はRESTでも起こるが、なぜとりわけ 「GraphQLでは起こりやすい」と言われるのか 21 type Article { title: String! }

    スキーマ定義 「スキーマで定義したfiledをどのように 返すか」を実装するresolver Article: { title: () => { // ここでDBから取得したArticle 1レコードが持つ // titleカラムのデータを返す処理 } } 素朴な実装をしていると、「Articleを複数一気に取得するようなQuery」が投げら れたとき、「Articleの1件取得」を何度も行ってしまう(N+1) 「単一のfieldをどのようにresolveするか」という実装と、「それが一気に複数回呼 ばれることがある」というユースケースの想定に思考のギャップが生まれやすい?
  22. #Offers_GraphQL実践LT 任意のクエリをクライアントが投げられることへの配慮 22 • スキーマ定義において、ネストした構造を作ることがで き、親-子-親という再帰できる構造も作りうる ◦ ex: 記事の著者 /

    著者が書いた記事一覧 • 結果として、「特定の記事の、著者の記事一覧の、それぞ れの著者の、記事一覧の...」というクエリが書けてしまう • クライアントが任意のクエリを投げられることで、ネスト があまりに深いクエリや、膨大なエンティティを取得しよ うとするクエリが、悪意を持つ者から投げられうる type Article { title: String! author: Author! } type Author { articles: [Article!]! }
  23. #Offers_GraphQL実践LT 「良さ」 「難しさ」 を踏まえた スタディサプリでの実際 03 23

  24. #Offers_GraphQL実践LT 前提: スタディサプリを取り巻く状況 24 • 「スタディサプリ」と一口に言っても、高校生向け/中学生向け/小学生向けのそ れぞれのプロダクト、ToBサービスにおけるプロダクト、学校の先生向け機能、 コーチングプランのコーチ向け機能、社内向け機能...など、数多くの事業ドメイ ンとそれに対応するマイクロサービス群がある •

    CTOや組織全体のリードアーキテクトはおらず、チームが裁量を持って個々の サービスについての意思決定を行うことが多い • 今日話すスタディサプリの事例も、社内でGraphQLを扱う3チームからのエ ピソードを持ち寄っている
  25. #Offers_GraphQL実践LT スキーマ駆動開発実例 25 figmaや GitHub issueで 画面仕様確認 Webフロントエンド/ iOS/Android/サーバサイドの チームメンバーでスキーマ定義

    スキーマを満たす resolverの実装 得られた型をもとに 画面の実装
  26. #Offers_GraphQL実践LT N+1はセオリー通りData Loaderによる対応が多い 26 • DBアクセスやHTTP requestなどのN+1を起こされたくない処理をbatch化 するData Loaderという仕組みを導入することが多い •

    「GraphQLだと逆にN+1に気を遣うので案外混入させない」という声も。 • 一方で、以下のようにスタンスが二分されるトピックであると最近知った ◦ 「デフォルトではData Loaderを導入しない」派 (場合によってData Loaderを入れてN+1を減らすことがチューニング) ◦ 「デフォルトでData Loaderを導入する」派 (場合によってData Loaderを外してオーバーヘッドを減らすことがチューニング)
  27. #Offers_GraphQL実践LT 参考: Data Loaderに対するスタンスのアンケート 27 票数少なくてすみません...

  28. #Offers_GraphQL実践LT 実践Observability: Datadogでのquery別tracking 28 各query/mutationを個別に Datadogで観察できるようにしている

  29. #Offers_GraphQL実践LT 実践Observability: /api/graphql/{query名} 29 • APIエンドポイント別にSLI/SLOを計測するという慣習を維持するため にクライアントは /api/graphql/{query名} を叩くという運用を行っ ているチームもいた

    • このとき /api/graphql/ 以下のルーティングはすべて無視されて、実 際にリクエストのハンドリングを行うのは単一のgraphql controller • 用途が限定されている場合においては、これで十分なケースもありそう
  30. #Offers_GraphQL実践LT 実践エラーハンドリング 30 • GraphQLの性質上、部分成功/失敗が起こり得ると書いたが、本当にそ の部分的な成功/失敗がユーザーにとって意味があるか(あるいは、シス テムが部分成功をサポートするコストメリットがあるか)というのは実は ユースケース次第。 • スタディサプリ中学講座では、いわゆるHTTPステータスコードが500番

    台になるようなものは"errors"に詰める / Mutationにおける400番 台になりそうなエラーは、Mutationの返り値型をErrorとのUnionにす る...という方針。
  31. #Offers_GraphQL実践LT 実践エラーハンドリング: 参考記事 31 ※スタディサプリ参画者のquramyさんの記事や、今日のイベント登壇者であるqsonaさ ん(は、実は元スタディサプリのGraphQL推進者だった!)による反応記事に、このあたり のエラーハンドリングに対する考え方が言語化されている https://note.com/qsona/n/n45ebcb391d0b https://quramy.medium.com/graphql-error-下から見るか- 横から見るか-3924880be51f

  32. #Offers_GraphQL実践LT あらかじめ登録されたオペレーション以外を弾く: Persisted Query 32 https://blog.studysapuri.jp/entry/2023/01/27/100000 • 本番環境ではpersisted queryによってあら かじめ登録されたクエリのみを許容している

    • クライアントコードにおける新たなクエリを persisted query化するコマンドや、漏れを検 知するGitHub Actionを利用 • HTTP GETでpersisted queryに付与され るパラメーターが長過ぎるエラーに対応したこ とも
  33. #Offers_GraphQL実践LT Internalなシステム連携におけるGraphQL API 33 • schema stitching: ◦ マイクロサービスの提供するスキーマを合成してapi-gatewayから提供 https://blog.studysapuri.jp/entry/2023/06/05/graphql_type_merging_1

  34. #Offers_GraphQL実践LT 個人的な感想: 難しかった...(特にType Merging) 34 https://the-guild.dev/graphql/stitching/docs/approaches/type-merging

  35. #Offers_GraphQL実践LT Internalなシステム連携におけるGraphQL API 35 • 一方で、Go製のマイクロサービス群におけるschema stichingや Federationの採用を検討した別チームは、利用しているライブラリではう まく扱えなさそうということがわかったとのこと。 •

    Ruby→RubyのInternalな通信において、OpenAPIよりもGraphQLを 好んで採用したチームも。
  36. #Offers_GraphQL実践LT 得られたインサイト 04 36

  37. #Offers_GraphQL実践LT スキーマ駆動開発ができる組織/関係性を築く 37 • 「クライアントが必要なものをリクエストする」という特性上、「何がスキー マにあってほしいか」というスキーマ定義は、クライアント開発の一貫とし てやれそうに思う(ときもある) • 一方で実際には、DB設計まで見越したGraphQLスキーマ設計をした方 がいいシーンもあり、結局クライアントとサーバサイドの開発者が膝を詰

    めて議論する必要があった • 理想的にはクライアントサイドとサーバサイドが同一チームでやれると良い し(もちろん、そういった区分すらなければ問題はない)、せめてロールの 違いを超えて議論がしやすい組織構造や関係性だと良さそう
  38. #Offers_GraphQL実践LT エコシステムへの依存に自覚的になる 38 • HTTPのハンドラをライブラリなしで書けるような言語もある中で GraphQLを選ぶということは(それなりに重い)依存先を1つ増やすこと • 言語によってGraphQLエコシステムの発達度合いは異なるので、選択でき るライブラリとやりたいことのバランスは確認したい ◦

    ex: その言語の型システム上の恩恵は受けられそう?Schema first or DSL first? SchemaのFederationはできる?サポートしているdirectiveは? • 「ビジネスロジック」やそれが守る「データ」に比べると、相対的にGraphQL エコシステムの方が廃れる可能性の方が高いので、ロジックとGraphQLの 密結合を避けた設計を意識したい
  39. #Offers_GraphQL実践LT 引き出して初めてうれしいGraphQLパワー 39 • 個人的にRESTの考え方は以前から好きでそれは今でも変わらない • 一方でGraphQLという複雑系を持ち込むことで、考えることが減った り、開発体験が良くなったりというメリットを確かに体感できた • ただし、(どんな技術でもそうだが)「GraphQLとりあえず入れてみた」だ

    けで得られるメリットは少なく、GraphQL特有の課題を乗り越える工夫 とチームの習熟が必要不可欠だった • バランス取る発言が目立ったかもしれませんが、僕は今ではすっかり GraphQL好きです
  40. #Offers_GraphQL実践LT ご清聴ありがとうございました! 40


[8]ページ先頭

©2009-2025 Movatter.jp