GraphQL

Fragment Colocationの素晴らしさを布教したい【GraphQL】

Fragment Colocationの素晴らしさを布教したい【GraphQL】

この記事では、GraphQLのFragment Colocationの素晴らしさを布教します。

 

この記事を読むことで、以下の状態になることができるでしょう。

  • Fragment Colocationとは何かを理解できる
  • Fragment Colocationのメリットを知ることができる
  • Fragment Colocationの使用例を見てイメージを深めることができる

 

この記事を読んだあなたが、

Fragment Colocationめっちゃええやん!

 

となればとても嬉しいです。

 

では始めます。

GraphQLのFragment Colocationとは何か?

Fragment Colocationとは、GraphQLの「Fragment」と設計の考え方の「Colocation」を組み合わせた言葉です。

それぞれを簡単に説明します。

GraphQLの「Fragment」とは何か?

GraphQLのFragmentとは、クエリで使用するデータをまとめることができる機能です。

例えば、以下のようなGraphQLのクエリがあるとします。

query {
  user1 {
    id
    name
    age
  }
  user2 {
    id
    name
    age
  }
}

 

このような場合、id, name, age をFragmentとしてまとめて、以下のように記述することができます。

query {
  user1 {
    ...UserFragment
  }
  user2 {
    ...UserFragment
  }
}

fragment UserFragment on User {
  id
  name
  age
}

 

このように、Fragmentを使用することで、取得するデータやクエリをより効率的に定義することができます。

 

Colocationとは何か?

Colocationとは、関連するモジュールやデータをなるべく近くに配置することで保守性を高めようとする設計思想のことです。

Reactの公式でも、以下のようにおすすめのファイル構成として紹介されています。

一般的には、よく一緒に変更するファイルを近くに置いておくのは良いアイディアです。この原則は、「コロケーション」と呼ばれます。

 

Fragment Colocationとは何か?

Fragment Colocationとは、GraphQLのFragmentを利用することで、Colocation(コンポーネントとコンポーネントに必要なデータを近くに配置すること)を実現しようとする設計の考え方のことです。

 

このFragment Colocationには、多くのメリットがあります。

次に、そのメリットについて解説していきます。

 

Fragment Colocationのメリットについて

Fragment Colocationには、主に以下のようなメリットがあります。

  • コンポーネントとコンポーネントで使うデータの依存関係が明確になるため、保守・改修がしやすくなる
  • コンポーネントで必要なデータに変更が生じたときに、Fragmentを修正するだけで良い(クエリは変える必要がない)ため、とても楽
  • GraphQL Code Generatorと併用することで、Fragmentと紐付くコンポーネントの型を生成できる
  • Fragmentとして定義することで、重複するデータを何度も取得することが無くなるため、効率がいい

 

色々とありますが、まとめると以下がメリットと言えるでしょう。

コンポーネントとデータの依存関係が明確になることで、コンポーネントの凝集度が高まり、保守・改修がしやすくなる

 

次に、よりメリットのイメージを掴むため、実例を交えて解説していきます。

 

Fragment Colocationの実例

最初に、Fragment colocationを適用しない場合の例を紹介します。

前提として、以下の記事に従ってバックエンドのGraphQLサーバーとフロントエンドの環境構築は終わっているものとします。

GraphQLとTypeScriptを使った開発の流れ
GraphQLとTypeScriptを使った開発の流れGraphQLとTypeScriptを使った開発の流れを解説します。...

 

Fragment Colocationを適用しない場合の実装例

Fragment Colocationを適用しない場合、components配下は以下のようなディレクトリ構成になっています。

Fragment Colocationを適用しない場合の実装例

 

それぞれのファイルの中身は以下の通りです。

 

parts/

ページを表示する際に使用するコンポーネントのファイルです。

Header, Body, Footerでそれぞれ作成しています。

Header/index.tsx
import { Article } from "../../../generated/graphql";

type Props = {
  article: Article;
};

export const Header = ({ article }: Props) => {
  const { title, author } = article;
  return (
    <>
      <div>タイトル:{title}</div>
      <div>執筆者:{author}</div>
    </>
  );
};

 

Body/index.tsx
import { Article } from "../../../generated/graphql";

type Props = {
  article: Article;
};

export const Body = ({ article }: Props) => {
  const { abstract, content } = article;
  return (
    <>
      <div>概要:{abstract}</div>
      <div>本文:{content}</div>
    </>
  );
};

 

Footer/index.tsx
import { Article } from "../../../generated/graphql";

type Props = {
  article: Article;
};

export const Footer = ({ article }: Props) => {
  const { title, author } = article;
  return (
    <>
      <div>タイトル:{title}</div>
      <div>執筆者:{author}</div>
    </>
  );
};

 

templates/ArticleContent/index.tsx

上記のコンポーネントを取りまとめてページを作るコンポーネントです。

import { Article } from "../../../generated/graphql";
import { Body } from "../../parts/Body";
import { Footer } from "../../parts/Footer";
import { Header } from "../../parts/Header";

type Props = {
  article: Article;
};

export const ArticleContent = ({ article }: Props) => {
  return (
    <div>
      <Header article={article} />
      <div>---------------------</div>
      <Body article={article} />
      <div>---------------------</div>
      <Footer article={article} />
    </div>
  );
};

 

templates/ArticleContent/index.graphql

コンポーネントで使用するデータを取得するためのGraphQLクエリを定義するファイルです。

query GetArticle($id: Int!) {
  article(id: $id) {
    abstract
    content
    title
    author
  }
}

 

Fragment Colocationを適用しない場合の良くない点

このような実装の場合、以下のような不便な点が生じます。

  • コンポーネントがどのデータを必要としているかが分かりにくい
    • 各コンポーネントを確認した上で、クエリで取得するデータを決めないといけないため変更が大変
  • コンポーネントで取得するデータに変更が生じた場合、クエリ(index.grqphql)を変更する必要がある
    • 変更範囲(影響範囲)が広い

 

コンポーネントとデータの依存関係が分かりにくく、コンポーネントの凝集度も低いため、保守・改修が難しくなるのが大きなデメリットと言えるでしょう。

 

Fragment Colocationを適用した場合の実装例

Fragment Colocationを適用した場合、components配下は以下のようなディレクトリ構成になります。

Fragment Colocationを適用した場合の実装例

 

大きな違いは、各コンポーネント(Header, Body, Footer)のファイルに、Fragmentのファイルが追加されている点です。

それぞれの実装は以下のようになります。

 

parts/Header

index.tsx

引数の型には、FragmentからGraphQL Code Generatorで生成したもの(HeaderArticleFragment)を指定します。

import { HeaderArticleFragment } from "../../../generated/graphql";

type Props = {
  fragment: HeaderArticleFragment;
};

export const Header = ({ fragment }: Props) => {
  const { title, author } = fragment;
  return (
    <>
      <div>タイトル:{title}</div>
      <div>執筆者:{author}</div>
    </>
  );
};

 

frargment.graphql

Headerで必要なデータのみをFragmentとして定義します。

fragment HeaderArticle on Article {
  title
  author
}

 

parts/Body

index.tsx
import { BodyArticleFragment } from "../../../generated/graphql";

type Props = {
  fragment: BodyArticleFragment;
};

export const Body = ({ fragment }: Props) => {
  const { abstract, content } = fragment;
  return (
    <>
      <div>概要:{abstract}</div>
      <div>本文:{content}</div>
    </>
  );
};

 

frargment.graphql
fragment BodyArticle on Article {
  abstract
  content
}

 

parts/Footer

index.tsx
import { FooterArticleFragment } from "../../../generated/graphql";

type Props = {
  fragment: FooterArticleFragment;
};

export const Footer = ({ fragment }: Props) => {
  const { title, author } = fragment;
  return (
    <>
      <div>タイトル:{title}</div>
      <div>執筆者:{author}</div>
    </>
  );
};

 

frargment.graphql
fragment FooterArticle on Article {
  title
  author
}

 

templates/ArticleContent/index.tsx

import { Article } from "../../../generated/graphql";
import { Body } from "../../parts/Body";
import { Footer } from "../../parts/Footer";
import { Header } from "../../parts/Header";

type Props = {
  article: Article;
};

export const ArticleContent = ({ article }: Props) => {
  return (
    <div>
      <Header fragment={article} />
      <div>---------------------</div>
      <Body fragment={article} />
      <div>---------------------</div>
      <Footer fragment={article} />
    </div>
  );
};

 

templates/ArticleContent/index.graphql

各コンポーネントのFragmentを取りまとめて、クエリを形成します。

query GetArticle($id: Int!) {
  article(id: $id) {
    ...HeaderArticle
    ...BodyArticle
    ...FooterArticle
  }
}

 

Fragment Colocationを適用した場合の良い点

このようにFragment Colocationを適用した実装に変えた場合、以下のようなメリットが生まれます。

  • コンポーネントとデータの依存関係が明確になる
  • コンポーネントに必要なデータに変更が生じても、Fragmentの定義を変えるだけで良い(クエリを変更する必要がない)
  • 型定義もFragmentを元に作成した型を使用しているため、変更する必要がない
  • Fragmentを利用することで、同じデータを重複して取得することはなくなる

 

最後のFragmentを利用することで、同じデータを重複して取得することはなくなるについて補足します。

例えば今回の場合、HeaderとFooterでは同じデータ(titleとauthor)を指定しています。

 

そのため、普通に考えたら同じデータが二つ取得されそうですが、Fragmentで指定した場合は、重複は排除されます。

 

実際に見てみましょう。

クエリの結果を出力した場合、以下のように表示されます。

GraphQLのFragmentでは重複は排除される

 

重複データはまとめられていることが分かるでしょう。

このように、重複データがまとめられるので、無駄にデータを取得することがない点もFragment Colocationのメリットと言えます。

 

Fragment Colocationの素晴らしさを布教したい:まとめ

今回は、GraphQLのFragment Colocationという考え方を紹介してきました。

Fragment Colocationには、以下のようなメリットがあります。

  • コンポーネントとコンポーネントの依存関係が明確になるため、保守・改修がしやすくなる
  • コンポーネントで必要なデータに変更が生じたときに、Fragmentを修正するだけで良い(クエリは変える必要がない)ため、とても楽
  • GraphQL Code Generatorと併用することで、Fragmentと紐付くコンポーネントの型を生成できる
  • Fragmentとして定義することで、重複するデータを何度も取得することは無くなるため、効率がいい

 

個人的には、GraphQLとReact等のコンポーネント開発をしている場合、適用しない理由がないほどの仕組みだと考えています。

 

この記事をきっかけにFragment Colocationを取り入れる人が一人でも増えたら嬉しいです。

最後まで読んでいただきありがとうございました。

 

Fragment Colocation 参考記事

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA