Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
GraphQL 「良さ」・「難しさ」 再探訪 〜スタディサプリにおける実例〜
Search
Fumina Chihama
February 08, 2024
1.1k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
GraphQL 「良さ」・「難しさ」 再探訪 〜スタディサプリにおける実例〜
Fumina Chihama
February 08, 2024
More Decks by Fumina Chihama
See All by Fumina Chihama
_配布資料商談力アップ_100社の経験に基づく初回商談の極意_Crevo.pdf
fumina
0
180
20241203_セミナー資料.pdf
fumina
0
150
"誰でも売れる"を体系的に整理!営業のプロが伝授する成功法則.pdf
fumina
0
81
Monoxer講演資料_書籍出版記念対談.pdf
fumina
0
130
DBの選び方LT
fumina
2
340
Azure OpenAI を活用して金融機関にお届けする LLM + RAG サービス
fumina
1
800
RAGを活用した動画学習コンテンツの推薦 ~実装の工夫と課題~
fumina
0
1.1k
RAGの基本と最新技術動向
fumina
0
1.4k
二刀流で切り開くRAG活用術
fumina
0
730
Featured
See All Featured
Leo the Paperboy
mayatellez
7
1.8k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
320
Amusing Abliteration
ianozsvald
1
210
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
330
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
410
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
180
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
190
Tell your own story through comics
letsgokoyo
1
950
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
180
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
210
Transcript
#Offers_GraphQL実践LT GraphQL 「良さ」・「難しさ」 再探訪 〜スタディサプリにおける実例〜 内山高広 @highwide 2024.02.08 #Offers_GraphQL実践LT
#Offers_GraphQL実践LT Agenda | 00 01 02 03 04 About Me
& Us GraphQL 「良さ」 再探訪 GraphQL 「難しさ」 再探訪 「良さ」「難しさ」を踏まえたスタディサプリでの実際 得られたインサイト 2
#Offers_GraphQL実践LT About Me & Us 00 3
#Offers_GraphQL実践LT こんにちは! • 内山 高広 / @highwide • スタディサプリでプロダクト基盤のWeb開発を行っています •
GraphQLを使っているチーム→使っているチーム→使っていないチー ム...と、スタディサプリの複数チームに携わってきました ◦ Ruby on Rails/node.js/Go/Reactなどを書いてきました • 長男(4歳)と次男(0歳)の子育てを楽しんでいます 4
#Offers_GraphQL実践LT 今日話す「スタディサプリ」について 5 スタディサプリブランドには様々 なサービスがありますが、今日は 学生の方に主に使っていただいて いるサービスについて話します。 https://studysapuri.jp/
#Offers_GraphQL実践LT GraphQL 「良さ」 再探訪 GraphQLを導入するメリットなんて、いろんなところで語られてますが改めて... 01 6
#Offers_GraphQL実践LT 出発地点: RESTfulなアーキテクチャとの比較から • RESTfulなアーキテクチャ: ここではURLで表現される「リソース」をHTTPメソッドで操作するステー トレスなAPI設計をイメージします ◦ ex: ▪
GET /articles => 記事一覧 ▪ GET /articles/{id} => 記事詳細 ▪ POST /articles => 記事作成 7
#Offers_GraphQL実践LT sample: 記事一覧画面 8 Awesome Media Article 1 summary Article
2 summary Article 3 summary GET /articles
#Offers_GraphQL実践LT sample: 記事一覧画面 9 Awesome Media Article 1 summary Article
2 summary Article 3 summary GET /articles …みなさんが作ってるシステム、 本当にこんなシンプルなやつですか?
#Offers_GraphQL実践LT たとえば、こういう感じだったりしませんか? 10 Awesome Media Article 1 summary / author
Articleに似せた記事広告 (システム的には別Entityとして表現されてる) 求人 情報 ユーザー名/前回ログイン日時 記事カテゴリ一覧(ユーザーの好みでパーソナライズされてる) 記事閲覧数 ランキング 注目の Author 天気 予報 写真 いろいろ 動画 いろいろ Article 2 これは、 「GET /articles」 ...で、いいのか?
#Offers_GraphQL実践LT 複数リソースで成り立つ画面/APIとの向き合い方あれこれ 11 • 複数のリソースの中から代表的なリソースに着目する ◦ 「複数リソースを取得する必要はあるが、あくまでこれは"記事一覧"だ」 • 複数のリソースによって構成される1つの(メタ)リソースを見出す ◦
「これはDashboardというリソースだ」 • リソース指向ではなく、画面指向やコンポーネント指向な命名をする ◦ 「内部的なAPIルーティングも /api/toppage にしよう」 • 単一リソースを返すシンプルなAPIをクライアントが必要に応じて複数呼ぶ ◦ 「/articles と /authors と /videos と...を非同期で呼ぶ」 ◦ ※ この発表では扱いきれないのですが、React Server Componentとの相性◎
#Offers_GraphQL実践LT その他、RESTfulな設計で直面する難しさ 12 • 同一リソースを扱うがコンテキストによって微妙に異なる表示項目 ◦ 導線や利用端末によって記事詳細ページで表示している情報が異なる仕様だが、 すべて「GET /articles/{id}」で取得している ▪
本来は不要なデータもまとめて取得してしまう "over-fetching" が起こっ ている • リソース指向よりもユースケース指向で表現したくなるような更新系API ◦ 「accountのactivateを行いたいが、それをリソースのPOST/PUTとして表現 するのはやや"座り"が悪い」
#Offers_GraphQL実践LT これらの課題に対しての、GraphQLという解決策 13 クライアント サーバー GraphQL スキーマ Query: 必要なエンティティの必要なフィールドを 1つのリクエストですべてクエリする
Mutation: 更新処理に必要なパラメータを渡し、必要 な返り値をクエリする (多くの場合) 単一のAPIエンドポイントで QueryやMutationを待ち受ける ex: /api/graphql
#Offers_GraphQL実践LT これらの課題に対しての、GraphQLという解決策 14 クライアント サーバー GraphQL スキーマ Query: 必要なエンティティの必要なフィールドを 1つのリクエストですべてクエリする
Mutation: 更新処理に必要なパラメータを渡し、必要 な返り値をクエリする (多くの場合) 単一のAPIエンドポイントで QueryやMutationを待ち受ける ex: /api/graphql over-fetchingの心配もなく、 一画面の表示にに大量のリクエ ストを発行する必要もなくなる リソース指向の APIエンドポイント設計は不要に
#Offers_GraphQL実践LT 「何を返す必要があるのか」の知識をクライアントに寄せられる 15 Awesome Media Article 1 summary / author
Articleに似せた記事広告 (システム的には別Entityとして表現されてる) 求人 情報 ユーザー名/前回ログイン日時 記事カテゴリ一覧(ユーザーの好みでパーソナライズされてる) 記事閲覧数 ランキング 注目の Author 天気 予報 写真 いろいろ 動画 いろいろ Article 2 ※ 仮に画面で表示したい項目が変 わっても、それが既にスキーマで定義 されたものならば、サーバサイドの開 発は不要 query TopPageQuery { article { title summary } adArticles { title summary } job { (以下略)
#Offers_GraphQL実践LT スキーマ駆動開発によるフロー効率の向上 16 クライアント サーバー GraphQL スキーマ 型の提供: スキーマが決まれば、型への変換ができるの で、サーバーの実装を待たず開発着手可能
resolverの実装: スキーマで定義されたエンティティや フィールドを実際に返せるような実装 ※ もちろん、クライアント-サーバー間のコントラクトとなるスキーマさえあればスキーマ駆 動開発はできるが、GraphQLスキーマのちょうど良い表現力や、クライアントの型に変換 するエコシステムの充実度合いは、開発体験をより良いものにしている (と、思う。最近だとTypeSpecのことはちょっと気になっている)
#Offers_GraphQL実践LT 02 17 GraphQL 「難しさ」 再探訪 「良さ」を理解することで、それに立脚した「難しさ」の解像度が上がるはず
#Offers_GraphQL実践LT 単一のエンドポイントに様々なユースケースのリクエストを 行うことによる、これまでの慣習の見直し 18 • Observability(観測性) ◦ SLI/SLOをHTTPエンドポイントごとに計測しているシステムは少なくないはず ◦ すべてのQueryやMutationを受け付ける
/api/graphql では、そのSuccess Rateを見ても、特定ユースケースの兆候はわからない • Authorization(認可) ◦ たとえばRailsのようなMVCフレームワークの場合、ルーティングに対応する個々 のControllerで認可を行うことが多く、そのためのライブラリも充実している ◦ すべてをgraphql_controllerでハンドリングすることになったとき、どのように 認可を行うべきか
#Offers_GraphQL実践LT 1つのリクエストで複数のエラーが起こりうることによるエ ラーハンドリングの難しさ 19 • 1つのリクエストで複数のエンティティの取得を行えるということは「Aの取得 には成功した」「Bの取得には失敗した」という部分成功/失敗が起こりうる • この部分成功/失敗を鑑みたうえで、以下のような選択を迫られる ◦
(これまで通り)HTTP status codeでエラーを表現する ◦ response bodyのerrorsというfieldにエラー情報を詰め込む ◦ エラーを示すエンティティをスキーマ上で定義する
#Offers_GraphQL実践LT N+1はRESTでも起こるが、なぜとりわけ 「GraphQLでは起こりやすい」と言われるのか 20 type Article { title: String! }
スキーマ定義 「スキーマで定義したfiledをどのように 返すか」を実装するresolver Article: { title: () => { // ここでDBから取得したArticle 1レコードが持つ // titleカラムのデータを返す処理 } }
#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するか」という実装と、「それが一気に複数回呼 ばれることがある」というユースケースの想定に思考のギャップが生まれやすい?
#Offers_GraphQL実践LT 任意のクエリをクライアントが投げられることへの配慮 22 • スキーマ定義において、ネストした構造を作ることがで き、親-子-親という再帰できる構造も作りうる ◦ ex: 記事の著者 /
著者が書いた記事一覧 • 結果として、「特定の記事の、著者の記事一覧の、それぞ れの著者の、記事一覧の...」というクエリが書けてしまう • クライアントが任意のクエリを投げられることで、ネスト があまりに深いクエリや、膨大なエンティティを取得しよ うとするクエリが、悪意を持つ者から投げられうる type Article { title: String! author: Author! } type Author { articles: [Article!]! }
#Offers_GraphQL実践LT 「良さ」 「難しさ」 を踏まえた スタディサプリでの実際 03 23
#Offers_GraphQL実践LT 前提: スタディサプリを取り巻く状況 24 • 「スタディサプリ」と一口に言っても、高校生向け/中学生向け/小学生向けのそ れぞれのプロダクト、ToBサービスにおけるプロダクト、学校の先生向け機能、 コーチングプランのコーチ向け機能、社内向け機能...など、数多くの事業ドメイ ンとそれに対応するマイクロサービス群がある •
CTOや組織全体のリードアーキテクトはおらず、チームが裁量を持って個々の サービスについての意思決定を行うことが多い • 今日話すスタディサプリの事例も、社内でGraphQLを扱う3チームからのエ ピソードを持ち寄っている
#Offers_GraphQL実践LT スキーマ駆動開発実例 25 figmaや GitHub issueで 画面仕様確認 Webフロントエンド/ iOS/Android/サーバサイドの チームメンバーでスキーマ定義
スキーマを満たす resolverの実装 得られた型をもとに 画面の実装
#Offers_GraphQL実践LT N+1はセオリー通りData Loaderによる対応が多い 26 • DBアクセスやHTTP requestなどのN+1を起こされたくない処理を batch化するData Loaderという仕組みを導入することが多い •
GraphQLのエコシステムの中に(詳細な仕組みは違えど)だいたいある • 一方で、以下のようにスタンスが二分されるトピックであると最近知った ◦ 「デフォルトではData Loaderを導入しない」派 (Data Loaderを入れることがチューニング) ◦ 「Data Loaderをデフォルトで入れるが、場合によってはオーバー ヘッドを嫌って外す箇所もある」派 (Data Loaderを外すことがチューニング)
#Offers_GraphQL実践LT 参考: Data Loaderに対するスタンスのアンケート 27 票数少なくてすみません...
#Offers_GraphQL実践LT 実践Observability: Datadogでのquery別tracking 28 各query/mutationを個別に Datadogで観察できるようにしている
#Offers_GraphQL実践LT 実践Observability: /api/graphql/{query名} 29 • APIエンドポイント別にSLI/SLOを計測するという慣習を維持するため にクライアントは /api/graphql/{query名} を叩くという運用を行っ ているチームもいた
• このとき /api/graphql/ 以下のルーティングはすべて無視されて、実 際にリクエストのハンドリングを行うのは単一のgraphql controller • 用途が限定されている場合においては、これで十分なケースもありそう
#Offers_GraphQL実践LT あらかじめ登録されたオペレーション以外を弾く: Persisted Query 30 https://blog.studysapuri.jp/entry/2023/01/27/100000 • 本番環境ではpersisted queryによってあら かじめ登録されたクエリのみを許容している
• クライアントコードにおける新たなクエリを persisted query化するコマンドや、漏れを検 知するGitHub Actionを利用 • HTTP GETでpersisted queryに付与され るパラメーターが長過ぎるエラーに対応したこ とも
#Offers_GraphQL実践LT Internalなシステム連携におけるGraphQL API 31 • schema stitching: ◦ マイクロサービスの提供するスキーマを合成してapi-gatewayから提供 https://blog.studysapuri.jp/entry/2023/06/05/graphql_type_merging_1
#Offers_GraphQL実践LT 個人的な感想: 難しかった...(特にType Merging) 32 https://the-guild.dev/graphql/stitching/docs/approaches/type-merging
#Offers_GraphQL実践LT Internalなシステム連携におけるGraphQL API 33 • 一方で、Go製のマイクロサービス群におけるschema stichingや Federationの採用を検討した別チームは、利用しているライブラリではう まく扱えなさそうということがわかったとのこと。 •
Ruby→RubyのInternalな通信において、OpenAPIよりもGraphQLを 好んで採用したチームも。
#Offers_GraphQL実践LT 得られたインサイト 04 34
#Offers_GraphQL実践LT スキーマ駆動開発するならば、関係性の構築を 35 • 「クライアントが必要なものをリクエストする」という特性上、「何がスキー マにあってほしいか」というスキーマ定義は、クライアント開発の一貫とし てやれそうに思う • 一方で実際には、DB設計まで見越したGraphQLスキーマ設計をした方 がいいシーンもあり、結局クライアントとサーバサイドの開発者が膝を詰め
て議論する必要があった • 理想的にはクライアントサイドとサーバサイドが同一チームでやれると良い し(もちろん、そういった区分すらなければ問題はない)、せめてロールの 違いを超えてこの手の議論がしやすい組織構造や関係性だと良さそう
#Offers_GraphQL実践LT エコシステムへの依存に自覚的になる 36 • HTTPのハンドラをライブラリなしで書けるような言語もある中で GraphQLを選ぶということは(それなりに重い)依存先を1つ増やすこと • 言語によってGraphQLエコシステムの発達度合いは異なるので、選択でき るライブラリとやりたいことのバランスは確認したい ◦
ex: その言語の型システム上の恩恵は受けられそう?Schema first or DSL first? SchemaのFederationはできる?サポートしているdirectiveは? • 「ビジネスロジック」やそれが守る「データ」に比べると、相対的にGraphQL エコシステムの方が廃れる可能性の方が高いので、ロジックとGraphQLの 密結合を避けた設計を意識したい
#Offers_GraphQL実践LT 引き出して初めてうれしいGraphQLパワー 37 • 個人的にRESTの考え方は以前から好きでそれは今でも変わらない • 一方でGraphQLという複雑系を持ち込むことで、考えることが減った り、開発体験が良くなったりというメリットを確かに体感できた • ただし、(どんな技術でもそうだが)「GraphQLとりあえず入れてみた」だ
けで得られるメリットは少なく、チームで習熟したうえでGraphQL特有 の課題を乗り越える工夫が必要不可欠だった • バランス取る発言が目立ったかもしれませんが、僕は今ではすっかり GraphQL好きです
#Offers_GraphQL実践LT ご清聴ありがとうございました! 38