Skip to content
This repository has been archived by the owner on Apr 8, 2020. It is now read-only.

フロントエンドからのGraphQLクエリを最適化した #134

Merged
merged 3 commits into from
Sep 8, 2018

Conversation

vvakame
Copy link
Member

@vvakame vvakame commented Sep 7, 2018

次のルールがあったほうがよさそうだなーと思ったので、それに沿って直してみている。

  • Queryを持っていいのは pages/ 配下のComponentのみ
    • 狙い: 画面あたりのクエリ数を1にする
    • それ以外のcontainersとかはFragmentのみ
  • Componentは直下の子Componentが要求するFragmentを自分のQuery or Fragmentに取り込む
    • 狙い: Componentをツリー構造として管理するため
    • 子孫の要求はFragmentとして集約できる
  • Componentは自分のJSX内で使うフィールドのみ要求する
    • 狙い: 子のためのフィールド要求をしはじめると管理が難しくなるため
    • filter を使うとより厳密にできそう(このPRではやらない
  • Fragmentの命名は (自分を使うComponent名)Fragment とする
    • 狙い: Fragment名からComponentにたどり着きやすくするため
  • Componentが要求するクエリ範囲は可能な限り最小化する
    • 狙い: 後から広くするのは簡単だが狭めるのは苦痛が大きいため
    • TimetableRowFragment が参考になるかも
  • 親Componentの型を参照しない
    • 狙い: ツリー構造でComponentを管理する場合、関心事は子Componentのみで完結する
    • 親Component関連の型を使ってしまうと責任範囲が明瞭でなく、わかりにくくリファクタリングが難しそうだと感じた

判断に悩む 🤔

  • 単なる土管的なComponentにもいちいちFragmentを用意するべきか?
    • 例: ContentSectionTopContentGrid を繋いでいるが、ContentSectionFragmentを定義するべきか?
    • Topの実装を見た時、なぜ ContentGridFragment が必要なのかパット見で判断できない
    • ContentSectionのコードにContentGridFragmentの型定義が出てきて微妙
      • 孫の型が出てくるのはかなり難解…
    • 僕の意見: いちいちFragment定義すればいいと思う
    • 現状の実装: 子孫Fragmentを直接参照している
      • VSCode上で該当子孫にジャンプはできる
      • 間に何が挟まっているのかは自明ではない←辛い気もする

} from '../../../graphql/generated/TimetableRowFragment';

export const TIMETABLE_ROW_FRAGMENT = gql`
fragment TimetableRowFragment on SessionConnection {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここで on Query として sessionList { ... } としなかったのは、取得件数の指定が上位Componentの責務であると考えたため。
そうすると、このComponentの責任範囲としては sessionList の返り値である SessionConnection に対する要求から書き始めるのが正しいはず。

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かいですがConnectionはページングの責務も含んでるんで、このRowに対しては Session そのものが責務として合ってるのかなと思いました

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

わかる。しかしsessionListをフィルタして妥当なデータを見つける処理がここに入っている構造上仕方ないのだ…(その構造が悪いのでリファクタリングしたほうがよさはある


export const TIMETABLE_SECTION_FRAGMENT = gql`
fragment TimetableSectionFragment on Query {
sessionList {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここで件数を指定したりできます。
将来的に ContentGrid と件数がズレた場合、aliasを設定する必要があるかも(めんどいね

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これはQueryの関数(?)自体をFragmentにしてるときに起きるケースとして面白いですね!!
今はいい考えが湧いてこないけどこういうことも起こるんだなぁなるほどなるほど

@@ -9,7 +9,7 @@
"lint": "tslint -c tslint.json -p tsconfig.json",
"lint:fix": "npm run lint -- --fix",
"precommit": "lint-staged",
"codegen": "apollo codegen:generate graphql/generated --schema ../schema.graphql --addTypename --target typescript --outputFlat"
"codegen": "rm -rf graphql/generated && apollo codegen:generate graphql/generated --schema ../schema.graphql --addTypename --target typescript --outputFlat"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm -rf しておかないと、消したFragmentやらのファイルが残ってて意図せずコンパイルが通ってしまう場合があったので型定義を生成するたびに一回消すようにしました。

Copy link
Collaborator

@sawa-zen sawa-zen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

めっちゃ勉強なりました 🙇 🙇

Copy link
Collaborator

@adwd adwd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

知見が得られるコードレビューで面白かったです 😂

<NewsSection data={data} />
<AboutSection />
<ContentSection data={data} />
<StyledTimetableSection data={data} />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここはdataがまるごと渡ってて違和感あるような。
でも子コンポーネントのPropsを見ると自分のFragmentになってて型としてはOKってことなんすね〜。
graphql-anywhereのfilterはこういうときに使うのかな?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まぁまぁわかる


const NewsSection: React.SFC = ({ ...props }) => (
interface Props {
data: NewsListFragment;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 一瞬ん?ってなったのはこれがバケツリレーしてるだけのコンポーネントだからか。。なるほどなるほど

}
`;

const NewsList: React.SFC<Props> = ({ ...props }) => (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: ({ data, ...props }) もしくは ({ data: { newsList }, ...props }) すればよいかも

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まかせた!(なげやり

} from '../../../graphql/generated/TimetableRowFragment';

export const TIMETABLE_ROW_FRAGMENT = gql`
fragment TimetableRowFragment on SessionConnection {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かいですがConnectionはページングの責務も含んでるんで、このRowに対しては Session そのものが責務として合ってるのかなと思いました


export const TIMETABLE_SECTION_FRAGMENT = gql`
fragment TimetableSectionFragment on Query {
sessionList {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これはQueryの関数(?)自体をFragmentにしてるときに起きるケースとして面白いですね!!
今はいい考えが湧いてこないけどこういうことも起こるんだなぁなるほどなるほど

@adwd
Copy link
Collaborator

adwd commented Sep 7, 2018

Componentは直下の子Componentが要求するFragmentを自分のQuery or Fragmentに取り込む

これはFragmentを使う上でやらないとデータが子に渡らないのでルールと言うか必須なのでは? 🤔

単なる土管的なComponentにもいちいちFragmentを用意するべきか?

ContentSectionでいうと、現状のComponent分割構成を見直すべきなのかも?と思った。 Section でラップするだけの責務を負っているのが ContentSection でその関係でいうと pages/index.tsx

<Section title="NEWS" id="news">
  <News data={filter(NewsFragment, data))} />
</Section>
<Section title="ABOUT" id="about">
  <About />
</Section>
<Section title="CONTENTS" id="contents">
  <Contents data={filter(ContentsFragment, data)} />
</Section>
<Section title="TIME TABLE" id="time_table">
  <TimeTable data={filter(TimeTableFragment, data)} />
</Section>

こうなってるとか。孫Fragment問題はやらない案に同意です。

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants