GraphQLにおけるクライアントキャッシュ戦略2023.03.15リクルート × BASE × バイセル 【第1回フロントエンド勉強会】React & GraphQL2023.03.15
View Slide
自己紹介名前:早瀬和輝出身:愛知県名古屋市経歴:BuySell Technologiesに2021年に新卒入社趣味:開発、マンガ、アニメ、ベース、バスケTwitter:@KazukiHayase
はじめに● 今回話すのはクライアント側のキャッシュについて● CDNなどのキャッシュについては触れないです
アジェンダキャッシュの仕組み01キャッシュにおける課題02課題解決へのアプローチ03まとめ04
キャッシュの仕組み● いくつかのGraphQL Clientにはキャッシュ機構が備わっている○ Apollo Client, Relay, urql● キャッシュを活用することで無駄なリクエストが減る● そのためにはキャッシュ機構の正しい理解が必要
キャッシュ機構において重要な要素データの正規化01キャッシュの利用条件02
データの正規化● レスポンスデータは正規化されてキャッシュに保存される● 正規化することで○ キャッシュへのアクセスが早くなる○ データサイズを小さくすることができる
正規化の流れ1. Queryの結果を個別のオブジェクトに分割2. 分割したオブジェクトに一意な識別子を割り当て3. フラットなデータ構造に格納
正規化の流れの例右図のようなSchemaとQueryを考える※ Apollo Clientを例に解説しますが、他のClientでも大枠の流れは同じです
正規化の流れの例Queryの実行結果として右図のようなレスポンスを受け取る
正規化の流れの例1. Queryの結果を個別のオブジェクトに分割2. 分割したオブジェクトに一意な識別子を割り当て3. フラットなデータ構造に格納
正規化の流れの例1. Queryの結果を個別のオブジェクトに分割2. 分割したオブジェクトに一意な識別子を割り当て3. フラットなデータ構造に格納Task:1Task:2Task:3
キャッシュの利用条件● データが全てキャッシュにある場合はキャッシュを利用● 一部でもデータがキャッシュにない場合はリクエストを実行
キャッシュの利用条件● FetchTasks→FetchTasks2○ キャッシュが利用できない○ リクエストは2回● FetchTasks2→FetchTasks○ キャッシュが利用できる○ リクエストは1回
キャッシュにおける課題一部でもデータがキャッシュにない場合はリクエストが実行されるQueryの定義によっては全くキャッシュが利用されない
キャッシュにおける課題逆に常にキャッシュが利用されるようにしようとすると考慮するべきことが多い
キャッシュにおける課題仮に常にキャッシュが利用されるようにしようとすると● Queryの実行順序を工夫する● オブジェクト単位でQueryをまとめる● アプリケーション全体でQueryを使い回す
できなくはないが、、
個人的にはデメリットの方が大きいと判断
キャッシュにおける課題● Queryの実行順序を工夫する○ →実行順序まで考慮するのは現実的ではない● オブジェクト単位でQueryをまとめる○ →オーバーフェッチにつながる、RESTとほぼ変わらない● アプリケーション全体でQueryを使い回す○ →Query変更時の影響範囲が広い
課題解決へのアプローチページ単位でのキャッシュ最適化01データを3種類に分類02
ページ単位でのキャッシュ最適化● ページ単位でキャッシュ最適化を考える● アプリケーション全体でのキャッシュの利用は考慮しない○ Queryによってはキャッシュが利用される場合もある
ページ単位でのキャッシュ最適化ページを跨いだキャッシュの利用を考慮しないことで● ページで使用するデータを宣言的に定義できる○ GraphQLの良さを最大限活かす● ページ同士が疎結合になる○ Queryの変更の影響範囲が閉じる
データを3種類に分類データを3種類に分類して、分類ごとにQueryを定義することでキャッシュを利用しやすくする
データを3種類に分類コンテンツデータ マスタデータ 汎用マスタデータ01 02 03
コンテンツデータ● コンテンツ表示用のデータ● アクションに応じてQueryを定義する○ e.g. 初回表示、検索、モーダル○ 1ページに複数のQueryが定義されていることもある● 同じアクションであればキャッシュが利用される
マスタデータ● マスタデータやメタデータなどのシステム的に必要なデータ● 最初のレンダリング時のみリクエストが必要● 2回目以降はキャッシュを利用する
汎用マスタデータ● 基本的には使用しない○ コンテンツデータ・マスタデータのみの運用をまずは考える● アプリケーション全体で利用するかつサイズの大きいデータ● どうしてもアプリケーション全体でキャッシュしたい際に使用● オーバーフェッチを許容
データ分類フローユーザーのアクションによって取得データが変わるか?コンテンツデータマスタデータ汎用マスタデータYesページごとで重複して取得する事にパフォーマンス上の懸念があるか?NoYes No
全体像PageComponentAPageComponentAContentQueryPageComponentAMasterQueryGeneralMasterQueryPageComponentBPageComponentBContentQueryPageComponentBMasterQueryページコンポーネントごとにコンテンツ・マスタデータのQueryを定義汎用マスタデータのQueryはコンポーネントの外で定義
データ分類の例タスク検索画面
コンテンツデータ● タスクの検索結果のデータ● 検索の度に表示内容が変わる● 同じ検索条件ならキャッシュを利用
マスタデータ● 検索で利用する選択肢データ● 検索結果に関係なくデータは同じ● 初回以降はキャッシュを利用
汎用マスタデータ● 検索で利用する選択肢データ● 数千規模のデータかつ他の画面でも使うと仮定● この画面のみの利用であればマスタデータに含める
まとめ● キャッシュの仕組みを踏まえた上での戦略○ ページ単位でのキャッシュ最適化○ データを3種類に分類● GraphQLの良さを生かしつつ、キャッシュも活用できる● ただし懸念はある○ 汎用データが増えすぎると今回紹介した課題が再度浮上する
THANK YOU