Slide 1

Slide 1 text

GraphQLのあまり知られていない魅力
 (スキーマの表現力編)
 
 
ZOZO Tech Talk #4 - Webフロントエンド×新規事業
 株式会社ZOZO
 計測プラットフォーム本部 計測システム部
 
 西田 雅博 Copyright © ZOZO, Inc.

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO
 計測プラットフォーム本部 計測システム部
 西田 雅博
 GraphQL+Reactを極めたい
 Webフロントエンドエンジニア
 
 Rustもやりたい
 
 2

Slide 3

Slide 3 text

© ZOZO, Inc. 目次
 1. GraphQLとは
 2. 一般的に知られているGraphQLのメリット
 3. たまたま実感した予想外のGraphQLのメリット
 4. こんなGraphQLスキーマです
 5. GraphQLスキーマの表現力から得られたメリット
 6. まとめ
 
 
 3

Slide 4

Slide 4 text

© ZOZO, Inc. GraphQLとは
 4

Slide 5

Slide 5 text

© ZOZO, Inc. GraphQLとは
 5 ● Meta(Facebook)が開発したクライアントが指定 したデータの形でサーバーがレスポンスを返す クエリ言語
 ● https://graphql.org/
 ● https://spec.graphql.org/October2021/
 
 
 


Slide 6

Slide 6 text

© ZOZO, Inc. 一般的に知られていそうなGraphQLのメリット
 ● 画面に必要なデータを取得するのに1度のリクエストで済む
 ● リクエスト、レスポンスに静的型が付くのでTypeScriptなどとの相性がいい
 ● GraphiQLやApollo Sandboxといったプレイグラウンドが便利
 ● Apollo FederationやSchema Stitchingなどマイクロサービスを束ねる仕組みがある
 
 
 
 ついでにデメリットも
 ● すべてが POST /graphql になるのでキャッシュや監視のやり方が変わる
 ● クライアント、サーバーともに実装が変わったりと学習コストが必要
 
 6

Slide 7

Slide 7 text

© ZOZO, Inc. たまたま実感した予想外のGraphQLのメリット
 ● ある新規事業の小さいサービスをREST APIからGraphQLに移行しようとしている
 ● それほど複雑なQuery, MutationがないのでGraphQL特有のメリットはあまりなく、練習くらい になると思っていた
 
 ところが、
 ● GraphQLスキーマの表現力が高く、REST APIでは表現しきれなかったサーバーとクライアン ト間のドメインロジックが表現できた
 
 7 次のスライドからZOZOMAT for Handsというとある小さいサービスと、そのサー ビスのドメインロジックを表現できたGraphQLスキーマを紹介します
 


Slide 8

Slide 8 text

© ZOZO, Inc. 8 ● 足のサイズを測ってピッタリのサイズの靴を買えるZOZOMATの指輪版
 


Slide 9

Slide 9 text

© ZOZO, Inc. 9 ● 足のサイズを測ってピッタリのサイズの靴を買えるZOZOMATの指輪版
 ● マットの上に手をおいて、音声のガイドに従ってスマホのカメラで色んな 方向から手を撮影すると最適な指輪のサイズを出してくれる
 ● まだ一般リリースしてなくてこっそりやってます
 
 


Slide 10

Slide 10 text

© ZOZO, Inc. ZOZOMAT for Handsの測定はステップや状態が多い
 ユーザーに色んな角度から手をスマホで撮影してもらう必要 がある
 
 1. 真上から撮影して左右どっちの手か、マットが平たく置い てあるかなどを判定する
 2. マットの色に沿っていろんな角度から手を撮影する
 3. 最後に真上からもう一度撮影して完了
 
 このステップごとにサーバーと通信していて、クライアントは今 どのステップにいるのか、次はどのステップかを知る必要があ る
 
 
 10

Slide 11

Slide 11 text

© ZOZO, Inc. ZOZOMAT for Handsはエラーがたくさんある
 
 
 ● 最初の真上から撮影するとき
 ○ 人差し指と中指の間隔が開きすぎている
 ○ カメラが手に近すぎる
 ● いろんな角度から撮影しているとき
 ○ 指定した色の方向から撮影していない
 ○ カメラの角度が高すぎる
 ● などなど
 ● それぞれ対応した音声ガイドを再生するのでエラーハン ドリングが重要
 
 
 11

Slide 12

Slide 12 text

© ZOZO, Inc. こんなGraphQLスキーマです
 12

Slide 13

Slide 13 text

© ZOZO, Inc. type Mutation { """ TODOを追加します """ addTodo(text: String!): Todo } type Todo { id: ID! text: String! status: TodoStatus! foo: FooUnion! } union FooUnion = BarType | BazType | QuxType GraphQLスキーマの基本的な読み方
 
 
 ● Mutation: 実行でデータが変化する操作
 ○ RESTでいうとPUT/POST/DELETE
 ○ QueryはGET
 ● addTodoミューテーション
 ○ NonNullなStringを引数にとって
 ○ NullableなTodoを返す
 ● type Todoで型を宣言
 ○ id, text, status, fooフィールドがある
 ○ どれもNonNull
 ● statusはTodoStatus enum
 ● FooUnionは3つのうちいずれかになる
 
 
 13 enum TodoStatus { TODO DONE }

Slide 14

Slide 14 text

© ZOZO, Inc. type Mutation { """ processSession 計測ステップを進行します。 sessionIdを指定し、撮影した画像をimageに入れて測定ステップを進めてください。 """ processSession(sessionId: ID!, image: Image!): ProcessSessionResult! } union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted どんなGraphQLスキーマになったか
 
 
 ※説明用に簡略化していますが実際のスキーマと基本的には同じです
 14

Slide 15

Slide 15 text

© ZOZO, Inc. type Mutation { """ processSession 計測ステップを進行します。 sessionIdを指定し、撮影した画像をimageに入れて測定ステップを進めてください。 """ processSession(sessionId: ID!, image: Image!): ProcessSessionResult! } union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted processSession ミューテーション
 
 
 15 ダブルクォート3つで囲んだところはdescription
 コメントのようなものだけどMarkdownも使えるれっきとしたドキュメント
 GraphQLの型で表現しきれないビジネスロジックは無理せずdescriptionを活用する


Slide 16

Slide 16 text

© ZOZO, Inc. type Mutation { """ processSession 計測ステップを進行します。 sessionIdを指定し、撮影した画像をimageに入れて測定ステップを進めてください。 """ processSession(sessionId: ID!, image: Image!): ProcessSessionResult! } union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted processSession ミューテーション
 
 
 16 descriptionに書いてあるとおりの処理を行うprocessSessionミューテーション
 ※GraphQLでファイルアップロードは現状できないので、graphql-multipart-requestを使います


Slide 17

Slide 17 text

© ZOZO, Inc. type Mutation { """ processSession 計測ステップを進行します。 sessionIdを指定し、撮影した画像をimageに入れて測定ステップを進めてください。 """ processSession(sessionId: ID!, image: Image!): ProcessSessionResult! } union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted processSession ミューテーション
 
 
 17 processSessionミューテーションの結果はセッションの途中、失敗、完了の3種類


Slide 18

Slide 18 text

© ZOZO, Inc. union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted type ProcessSessionProgressed { nextInstruction: CaptureInstruction! } type ProcessSessionFailed { failure: ProcessSessionFailure! } type ProcessSessionCompleted { # このIDで session Query を実行すると測定結果が取得できます。 completedSessionId: ID! } ProcessSessionResult ユニオン
 18 processSessionミューテーションの結果は
 セッションの途中、失敗、完了の3種類


Slide 19

Slide 19 text

© ZOZO, Inc. union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted type ProcessSessionProgressed { nextInstruction: CaptureInstruction! } type ProcessSessionFailed { failure: ProcessSessionFailure! } type ProcessSessionCompleted { # このIDで session Query を実行すると測定結果が取得できます。 completedSessionId: ID! } ProcessSessionResult ユニオン
 19 セッションの途中の場合は次の測定の指示
 
 enum CaptureInstruction { TOP BLUE # ... }

Slide 20

Slide 20 text

© ZOZO, Inc. union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted type ProcessSessionProgressed { nextInstruction: CaptureInstruction! } type ProcessSessionFailed { failure: ProcessSessionFailure! } type ProcessSessionCompleted { # このIDで session Query を実行すると測定結果が取得できます。 completedSessionId: ID! } ProcessSessionResult ユニオン
 20 測定ステップ失敗の場合はその理由
 enum ProcessSessionFailure { CAMERA_PITCH_TOO_LOW DISTANCE_TOO_CLOSE # ... }

Slide 21

Slide 21 text

© ZOZO, Inc. union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted type ProcessSessionProgressed { nextInstruction: CaptureInstruction! } type ProcessSessionFailed { failure: ProcessSessionFailure! } type ProcessSessionCompleted { # このIDで session Query を実行すると測定結果が取得できます。 completedSessionId: ID! } ProcessSessionResult ユニオン
 21 測定完了の場合は結果を取得するためのID
 (結果を出すのに数秒かかるので終わったことだけ通知する)


Slide 22

Slide 22 text

© ZOZO, Inc. type Mutation { """ processSession 計測ステップを進行します。 sessionIdを指定し、撮影した画像をimageに入れて測定ステップを進めてください。 """ processSession(sessionId: ID!, image: Image!): ProcessSessionResult! } union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted ここまでのまとめ
 ● processSessionを繰り返し使って計測を進める
 ● processSessionは3パターンの結果を返す
 ● 計測途中、失敗、完了それぞれ次の動作に必要な情報 が入っている
 
 
 22

Slide 23

Slide 23 text

© ZOZO, Inc. type Query { """ 計測が完了したセッションを取得します。 """ session(id: ID!): Session } type Session { id: ID! analysis: SessionAnalysis! } union SessionAnalysis = AnalysisInProgress | AnalysisCompleted | AnalysisFailed 測定結果を取得する session クエリ
 23

Slide 24

Slide 24 text

© ZOZO, Inc. type Query { """ 計測が完了したセッションを取得します。 """ session(id: ID!): Session } type Session { id: ID! analysis: SessionAnalysis! } union SessionAnalysis = AnalysisInProgress | AnalysisCompleted | AnalysisFailed 測定結果を取得する session クエリ
 24 測定の結果が分析途中、分析完了、分析失敗の3パターンにな ることがわかる。
 それぞれの型の中身は省略するが、途中なら数秒後クエリを再 実行、完了なら結果を表示、失敗ならエラー表示すれば良いこ とがわかる
 
 


Slide 25

Slide 25 text

© ZOZO, Inc. type Mutation { processSession(sessionId: ID!, image: Image!): ProcessSessionResult! } union ProcessSessionResult = ProcessSessionProgressed | ProcessSessionFailed | ProcessSessionCompleted type Query { session(id: ID!): Session } union SessionAnalysis = AnalysisInProgress | AnalysisCompleted | AnalysisFailed 測定の一連の流れがスキーマから読み取れる
 25

Slide 26

Slide 26 text

© ZOZO, Inc. GraphQLスキーマの表現力から得ら れたメリット
 26

Slide 27

Slide 27 text

© ZOZO, Inc. ドメインモデリングともいえる議論が活発にできた
 ● 最初はREST APIをそのまま移したようなスキーマだったがGitHubのPR上で100件近いコメン トで盛んな議論ができた🔥
 
 27

Slide 28

Slide 28 text

© ZOZO, Inc. ドメインモデリングともいえる議論が活発にできた
 ● 最初はREST APIをそのまま移したようなスキーマだったがGitHubのPR上で100件近いコメン トで盛んな議論ができた🔥
 ● 紹介したスキーマは割とシンプルですが原型はかなり違った
 ○ Union、EnumといったGraphQLの型システムの恩恵
 ○ 今回は使ってないですがディレクティブも強力です
 ● 以前のREST APIのときは入社間もないこともあって正直言ってあまりドメインを詳細まで理 解できていなかったが、この議論とGraphQLスキーマからよく理解できた
 
 
 28

Slide 29

Slide 29 text

© ZOZO, Inc. DDDのドメインモデリングにも使えるのではないか
 ● 先日読んだDomain Modeling Made Functionalという本はF#の型システムを利用してドメイン モデリングしていた
 ● GraphQLくらいの型システムでも実現可能と思える
 ● マイクロサービスが境界づけられたコンテキストに該当するならそのコンテキストが外部に提 供する能力を可読性高く表現できる…気がする
 ● GraphiQLやApollo Studioで動作が確認しやすいこと、スキーマのドキュメント性が高いことも ドメインエキスパートにやさしいのでは
 
 29

Slide 30

Slide 30 text

© ZOZO, Inc. まとめ
 ● 小さなサービスにGraphQLを導入した
 ● 予想していなかった効果として、スキーマの表現力によってドメインに関する議論が盛んに 行えた
 ● GraphQLおすすめです!
 
 
 30

Slide 31

Slide 31 text

No content