GraphQLでグラフ構造を構築する時にスキーマに含めない方が良いデータ
IT技術
はじめに
本日はGraphQLでグラフ構造を構築する時にスキーマに含めない方が良いデータを紹介させていただきます。
GraphQLでグラフ構造を構築する時に、オブジェクト型を定義しオブジェクトに関連があるフィールドを追加していく思います。
ただなにも考えずに関連があるフィールドを追加すると扱いがやっかいになるフィールドが出てくるかもしません。
そのようなデータは一言で言うと以下のようなデータです。
上記の内容だけではよくわからないかと思いますので、実際に上記の値を含めたスキーマを定義し、起こった問題と解決した方法をご紹介させていただきます。
例として、とある通販サイトがあると仮定して話を進めていきます。
目次
- 含めない方が良いデータ
- 含めない方が良いデータを含めたことで起こる問題
- 実際に解決した方法
含めない方が良いデータ
ある通販サイトでは、カテゴリーから商品種別絞り込んで目当ての商品を検索できるとします。
その時に「価格」や「レビュー評価数」などからさらに該当商品を絞り込うことができるとします。
カテゴリーごとのヒット数を取得したいときはどうすれば良いでしょうか?
例として以下のようなクエリを定義しました。
スキーマ
クエリ
レスポンス
カテゴリとそのサブカテゴリの商品数を取得することができました。
一見綺麗なグラフ構造になっていますが、「CategoryType」の「itemCountフィールド」が含めない方が良いフィールドです。
含めない方が良いデータを含めたことで起こる問題
先ほど定義したカテゴリのスキーマは以下のようになっていました。
問題なのは、「itemCountフィールド」です。
このフィールドのデータは検索条件により、値が変動するフィールドです。
つまり、「itemCountフィールド」は「検索条件に依存している」ということになります。
CategoryTypeを他のクエリで使用する場合はどうなるでしょうか?
ユーザーはお気に入りカテゴリとしてCategoryTypeのデータを保持しています。
この時のCategoryTypeのitemCountフィールドは一体どんな条件で取得すれば良いのでしょうか?
「検索条件を指定しなかった時の商品数?」でしょうか。
ただ、ユーザーのお気に入りカテゴリには商品数というデータ自体必要ないかもしれません。
CategoryTypeにそのデータを含めたことにより、データを必要としていないクエリからでも商品数を取得できるスキーマになってしまっています。
では以下のように「itemCountフィールド」がある場合とない場合で型を2つ作るという手もあるかもしれません。
上記のようにすれば、商品数を必要としてないクエリには、「CategoryType」を指定すれば、itemCountフィールドを指定される問題は回避できそうです。
では、以下のような要件があった場合はどうでしょうか?
◾️ カテゴリは商品数が必要だけど、サブカテゴリには必要ない。
◾️ カテゴリは商品数が必要ないけど、サブカテゴリには必要。
実際に上記のような要件があるかはわかりませんが、ないとは言えません。
その場合は素直に型を定義すると以下のようになります。(型名ツッコミどころあるかもしれませんが、例のためご容赦ください)
上記のように色んなパターンを定義すれば、「itemCount」が必要な時と必要でない時に合わせた型を使用して対応できそうですが、なんか似たような型が多すぎてよくわからなくなってきますね。
あまり良いスキーマではなさそうです。
他の方法として、インターフェースを使用してみる方法もあります。
以下定義例になります。
「CategoryType」と「CategoryWithItemCountType」の「subcategories」にはインタフェースを指定しました。
これにより、「subcategories」は「CategoryType」と「CategoryWithItemCountType」どちらの型でも返せるようになり型の数が少なくなりました。
ただ1つ懸念点があります。
「subcategories」で商品数を取得したい時にも商品数がない「CategoryType」がスキーマ上では返せるようになってしまいます。
実際にページ上に商品数を表示したい時に、期待していない型が返ってくる可能性があるのはよくないですね。
「CategoryWithItemCountType」を期待しているのに「CategoryType」で返ってきてしまった時の処理を考えなくてはいけません。
これもあまり良いスキーマではなさそうです。
実際に解決した方法
問題なのはCategoryTypeの中に外部パラメーターに依存するitemCountフィールドが存在することです。
これが存在することで色んな懸念点が生まれるならば完全に型を分離しましょう。
CategoryTypeからitemCountフィールドを分離し、ItemCountTypeとして新たにスキーマを定義しました。
フィールドを分離したことで以下のように別々のクエリでカテゴリ情報と、商品数を受け取ります。
クエリ
レスポンス
あとはデータを受け取った側でcodeの値でマッピングすれば、カテゴリごとに商品数を表示することができます。
CategoryTypeからitemCountフィールドを除去したことでかなり扱いやすいスキーマになりました。
UserType内に、CategoryTypeが存在してもまったく問題ないです。
ItemCountフィールド以外の情報は「名前やカテゴリコード」などカテゴリ自身が持つ静的な値なので、どこから呼ばれてもモデルのパラメーターとしてデータを返せるためになります。
このことから以下のことがわかりました。
- 関連があるデータをなんでもオブジェクト型に含めれば良いと言うわけでなさそう
- オブジェクト型に含めるフィールドは、以下のようなデータの場合に追加した方が良さそう
- オブジェクト自身が持つパラメーター(nameやcodeなど)
- オブジェクトが持つパラメーターを使用して取得できるデータ
- 「CategoryType」の「subcategoriesフィールド」はCategoryのcodeの値から取って来れそうなど(データの設計にはよります)
最後に
いかがだったでしょうか?
GraphQLはデータをグラフ構造で直感的に取得できるメリットがある反面、スキーマ設計がかなり難しいです。
実際にスキーマ設計をして問題に直面していかないと設計能力は向上していかないなと感じています。
今回ご紹介させていただいた内容がプロジェクトによっては、必ずしも解決策として当てはまらない場合もあるかもしれませんが一つの参考になれば良いかなと思っています。
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
服部晋平と申します! 前職では映像業や配送業に携わっていました。 趣味は、バイクツーリングに行ったり、美味しいラーメン屋巡りです。 未経験という身で入社させていただいたので、人一倍努力して頑張っていきたいと思います!