6.8K Views
July 13, 23
スライド概要
クライアント側にロジックを集中させると、Micro Service 化で細かく分かれたサーバー毎にクライアントと通信を行い、クライアント側でデータを統合したりサーバー間の依存関係を考慮した処理を書く必要があり、複雑で開発の難易度が上がったり、ログの収集が難しくなりがちです。
HireRoo ではこの課題に対して、GraphQLを活用しつつ Backend For Frontend なアーキテクチャに構成を変える取り組みを行いました。この構成変更により、サーバー間の依存関係を把握しやすい状態で処理ロジックを書けたり、データやログの収集や統合を簡易に行えるようになるなど、様々な改善を実施できました。
本登壇では、クライアント側にロジックを集中させていた HireRoo が Backend for Frontend をどのように導入していったか、導入して具体的にどんな改善となったか、経験した内容をご紹介します。
GraphQLを活用した Backend for Frontendへ リアーキテクチャした話 @Asaki Iwata 1
イントロダクション 自己紹介 Asaki Iwata @gajumaru0x SNS ● ● GitHub: asaki15 Twitter: gajumaru0x 前職 ● freee株式会社 ロードバイクを最近買いました © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 2
Agenda 1 フロントエンドとバックエンドの境界線 境界線における課題 における課題 2 Backend For Frontend とGraphQLとは? Backend For Frontend とGraphQLとは 3 課題解決をどう実現したか GraphQLを活用したBackend For Frontendへ 今後の課題 4 まとめ
01 フロントエンドとバックエンドの 境界線における課題
フロントエンドとバックエンドの境界線における課題 境界線における課題① ● サーバーサイドのMicroservice間で依存関係 ○ 特定のサービス間で密結合に ○ データの集約がMicroService Aに依存してしまっている Microservice B エラー発生! Microservice A MicroService C Frontend Microservice D Microservice E © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 5
フロントエンドとバックエンドの境界線における課題 境界線における課題① ● サーバーサイドのMicroservice間で依存関係 ○ 特定のサービス間で密結合に ○ データの集約がMicroService Aに依存してしまっている Microservice B 特定のMicroserviceで不具合が発生した場 合、それに依存するサービスの情報も取得 できなくなってしまう MicroService A MicroService C Frontend Microservice D Microservice E © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 6
フロントエンドとバックエンドの境界線における課題 境界線における課題① ● サーバーサイドのMicroservice間で依存関係 ○ 特定のサービス間で密結合に ○ データの集約がMicroService Aに依存してしまっている Microservice B 本来であれば、Microservice Cに不具合が発 生してもMicroservice AとMicroservice Bの 情報は表示させておきたい Microservice A MicroService C Frontend Microservice D Microservice E © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 7
フロントエンドとバックエンドの境界線における課題 境界線における課題① ● 依存関係のあるデータをクライアントサイドで集約しても複雑な処理になってしまいがち ○ 例:BとCのデータを取得した後にAを取得して集約させる ■ Bのデータを取得→エラーがあったら例外処理 ● Cのデータを取得→エラーがあったら例外処理 ○ Aのデータを取得して、集約 集約のロジックが膨れ上がり複雑になりがち Frontend Microservice B Microservice A Microservice C © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 8
フロントエンドとバックエンドの境界線における課題 境界線における課題② ● ● ● バックエンド側のデータ構造とフロントエンドで扱いたいデータ構造が必ずしも一致しない UIなどの変更が多いフロントエンドの都合でバックエンドも変えようとするとAPIの合意形成を取る必要があり、 コミュニケーションコストがかかる UIの変更がバックエンド側に依存してしまう 表示させたい情報はデータセットAの試験 名のみ Microservice B UI MicroService A 試験情報のデータセットA MicroService C 試験名 期限 複数の問題 etc © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 9
フロントとバックエンドの境界線における課題 課題のまとめ 課題 ① ● バックエンドでもフロントエンドでもデータの集約ロジックが複雑になりがち ② ● フロントエンドで扱いたいデータ構造とバックエンドのデータ構造の不一致によるコミュニケーション コストやバックエンドとフロントエンドの依存 GraphQLを活用したBackend For Frontendへリアーキテクチャすることで解消 © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 10
02 Backend For Frontend と GraphQLとは
Backend For Frontendとは? ● ● ● Backend For Frontend (BFF)はアーキテクチャ設計パターンの一つ ○ フロントエンドとバックエンドの中間に配置され双方向の複雑な処理を緩和させる責務を持つ フロントエンドのリクエストに応じて各種のAPIコール バックエンドから取得した内容を加工してフロントエンドに返すフロントエンド専用サーバー Microservice Frontend BFF Microservice Microservice © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 12
GraphQLとは? ● ● ● Overfetch・Underfetchの解消 ○ 必要な値だけをクエリに記述するので余計なフィー ルドの値を取得しないで済む ○ 一度に必要な情報の入れ忘れ・漏れを防ぎ取得でき る 単一エンドポイントにクエリを送り、jsonのレスポンスを 取得 型システムを提供 ○ APIインターフェースで使用する型の定義が可能 © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 13
GraphQLを活用したBackend For Frontend ● ● ● データ集約のロジックがBackend For Frontend (BFF)の責務に GraphQLを活用することでグラフ構造のまま、データを扱えるためロジックがシンプルになる APIエンドポイントは一つに GraphQLによって必要なデータだけを グラフ構造で定義できる Microservice Frontend BFF Microservice Microservice © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 14
03 課題解決をどう実現したか
課題解決をどう実現したか 技術スタック ● ● バックエンド ○ Microservice ○ Go ○ gRPC ○ PostgreSQL フロントエンド ○ TypeScript ○ React © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 16
課題解決をどう実現したか HireRooのアーキテクチャをBFFにする前の境界線 Backend Microservice B Microservice D Microservice C Microservice E Microservice A データ集約のロジックがバックエ ンド API Gateway Frontend Frontend © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 17
課題解決をどう実現したか HireRooのアーキテクチャをBFFにした後の境界線 Backend Microservice B Microservice D Microservice C Microservice E Microservice A Backend For Frontend 依存の合ったMicroserviceはシンプ ルに データ集約のロジックをBFF側に 寄せることが可能に API Gateway GraphQLによってまとまった単位 データ取得可能に Frontend Frontend © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 18
GraphQLを活用してデータをまとまった単位で取得可能に ● データをまとまった単位でGraph構造で取得可能に ○ 表示させたいデータはAとBの情報の場合 ■ Graph構造のまま、フロントエンドで使える ■ 表示させたい必要なフィールドだけを型として定 義 ● →GraphQL Fragment © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 19
Page 課題解決をどう実現したか Fragmentをどの単位で定義しているか ● Page・WidgetごとにFragmentを定義 ○ Page/Widgetは「単体で動く機能」の単位 ○ サイズ: Page > Widget ○ このPage/Widget という単位は ClientのComponentの集合でも使われている ■ 詳しくはHireRoo Tech Talk #1 Widget単位で必要な情報のみを取得 必要な項目だけをFragmentとして定義 しておく © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 20
課題解決をどう実現したか Page/Widget 単位で必要な情報を取得 Backend Microservice B Microservice D Microservice C Microservice E Microservice A Backend For Frontend API Gateway Page1のWidget1と同じFragment・同じ queryを使ってデータを取得できる Page 1 Frontend Widget 1 © HireRoo, Inc. Widget 2 Page 2 Page 3 Widget 1 GraphQLを活用した Backend for Frontend へリアーキテクチャした話 21
試験の形式が違っても 同じWidgetで構成されている © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 22
課題解決をどう実現したか 1度にPage/Widget 単位で必要な情報を取得すると何が嬉しいのか ● 単体で動く機能の単位で情報を取得していると再利用性が高まる ● 定義したFragmentを型情報としてフロントエンドで利用できるため、データとコンポーネントの依存関係が明確 ● 余計なデータが入ることを防ぎ、依存の広がりを抑制できる フロントエンドに都合の良いデータ取得が可能 (まさしくBackend For Frontend) BFFの具体的な実装例はこちら (https://hireroo.io/journal/tech/graphql-express-implementation) © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 23
課題解決をどう実現したか 課題のおさらい 課題 ① ● バックエンドでもフロントエンドでもデータの集約ロジックが複雑になりがち ② ● フロントエンドで扱いたいデータ構造とバックエンドのデータ構造の不一致によるコミュニケーション コストやバックエンドとフロントエンドの依存 © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 24
課題解決をどう実現したか GraphQLを活用したBackend For Frontendの導入によって得られたこと 課題 ① ● バックエンドでもフロントエンドでもデータの集約ロジックが複雑になりがち ② ● フロントエンドで扱いたいデータ構造とバックエンドのデータ構造の不一致によるコミュニケーション コストやバックエンドとフロントエンドの依存 → GraphQLを活用したBackend For Frontendの導入 ● ● ● フロントエンドとバックエンドで責務が分離された ○ バックエンド間の密結合を解消しやすくなった ■ 依存関係のあるデータの集約をBFFで担保 ○ フロントエンド側で複数のリクエストを組み立てる処理がBFF側に集約され、コードがシンプルになった フロントの都合で独立して作業がしやすくなった Backendの動作確認もローカルのBFFを通して動作確認しやすくなった © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 25
課題解決をどう実現したか 今後の課題と展望 ● ● ● GraphQL Schema定義の運用コストは増えた ○ →Protocol BufferからSchema情報を抽出し、できる部分を自動化 Microservice間の密結合を解消(進行中) テスト(プレビュー)環境でのBFFのデプロイ競合 ○ →環境に応じてリクエストを振り分ける仕組みを作る © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 26
04 まとめ
まとめ ● ● ● ● ● GraphQLを活用してBackend For Frontendを導入 GraphQLのFragmentはHireRooだとPage/Widgetの単位で定義 課題:MicroService間で依存関係があり、APIの組み立ての部分で処理が複雑になってしまう ○ →依存関係をシンプルに把握できるようになり、対応が簡単になった 課題:フロントエンドで扱いたいデータ構造とバックエンドのデータ構造の不一致によるコミュ ニケーションコストやバックエンドとフロントエンドの依存 ○ →フロントの都合で独立して作業がしやすくなった まだまだ改善したいことがたくさんあるので興味ある人はぜひ一緒に改善していきませんか? © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 28
ご清聴ありがとうございました 29
Memo(後で消す) ● ● ● ● ● ● ● ● 密結合状態があります ○ アーキテクチャ的にあった ○ 技術スタックなどのBefore / After BFFを入れたことによって密結合を解消したよ ○ このへんとかでgrpc over GraphQLになってるとか GraphQLを使ってデータをまとまった単位で取るようにしたよ まとまった単位のことをFragmentというよ FramgnetはWidget/Pageという単位でとっているよ ○ Widget/Pageというのは「単体で動く機能」の単位だよ ■ これ以上Presentation層の話を知りたければtech Tlkas #1をみてね ■ https://hireroo.connpass.com/event/277814/ BFFのfor Frontendの部分はFragmentにあるよ 元々の状態はこうだったけど、BFFの導入によって解消されたよ まとめ ○ BFFはGraphQLを使って導入したよ ○ GraphQLのFragmentは我々のサービスだと〜という単位で作っているよ ○ 密結合解消!最高! ○ これがfor Frontendだよ © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 30
まとめ ● ● ● GraphQLを活用したBackend For Frontendへのリアーキテクチャ ○ フロントエンドとバックエンドの責務を分離することで開発体験が良くなった まだまだ改善の余地がある 興味ある人はぜひ一緒に改善していきませんか? サーバサイドのMicroService 間で依存関係があり、その依存関係を Client 側でも認識し、とあるサービスが落ちた場合 に、MicroService 間の依存関係を考慮した処理を Client 側で書かなければならず、複雑で対応が難しい状態だったのが 解消して、依存関係をシンプルに把握できるようになり、対応が簡単になった ユーザー体験のどこにペインポイントがあるのか、分析を横断的に行いたい一方で、Client側での処理だけだと、分析用 のログ取得が難しく、分析しづらかったのが、分析用のログを取得しやすく、横断的な分析がしやすくなる予定! © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 31
課題解決をどう実現したか Fragment の活用 ● ● ● ● GraphQLのFragmentとは ○ Queryの中で使用するフィールドを再利用可能なセットとして切り出して定義できる機能 Widget ○ 1画面内に複数のComponentで構成されている ■ 例:一覧ページの検索部分で1 Widget Widget・Pageごとに専用のFragmentを定義 必要なデータをPage・Widget単位でFragmentをまとめて1つのqueryで取得 Page A Page Aに必要なデータを query Widget A Page/Widgetに対応した Fragmentをまとめて返す BFF Widget B Widget Bに必要なデータを query © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 32
課題解決をどう実現したか Fragment Collocation ● ● ● ● コンポーネントとデータの依存関係が明確に Fragmentを元に作成した型をコンポーネントのPropsやStateで使えるため、保守性が上がる 重複するデータをまとめてくれる © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 33
課題解決をどう実現したか なぜGraphQLを活用したBackend For Frontendを導入したのか ● ● UIに必要なデータを過不足なく取得可能 フロントエンドとバックエンドの関心を分離 ○ 依存関係のあるサービスを疎結合にしやすくする ● © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 34
課題解決をどう実現したか GraphQLを活用したBackend For Frontendの導入によって得られたこと GraphQLを活用したBFFの導入によって課題1, 2ともに解消できる状態に! ● ● ● フロントエンドとバックエンドで責務が分離された ○ バックエンド間の密結合を解消しやすくなった ■ 依存関係のあるデータの集約をBFFで担保 ○ フロントエンド側で複数のリクエストを組み立てる処理がBFF側に集約され、コードがシンプルになった フロントエンド都合のデータ管理が可能になることでフロントエンドの開発体験が向上した Backendの動作確認もローカルのBFFを通して動作確認しやすくなった © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 35
課題解決をどう実現したか GraphQLによってまとまった単位で情報を取得 ● Widget ○ 意味のあるまとまったComponentの集合 ○ Atomic Designで言うmolecules・organismsに近い ○ このWidgetの単位で表示させるために必要なデータを取得する © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 36
課題解決をどう実現したか Fragmentをどの単位で定義しているか ● Page・Widgetという単位でFragmentを定義 ○ Page/Widgetは「単体で動く機能」の単位 ○ サイズ: Page > Widget ○ このPage/Widget という単位はフロントエンドのPresentation層でも使われている ■ 詳しくはHireRoo Tech Talk #1 © HireRoo, Inc. Widget単位で必要な情報のみを取得 必要な項目だけをFragmentとして定義 しておく GraphQLを活用した Backend for Frontend へリアーキテクチャした話 37
課題解決に向けたリアーキテクチャ フロントエンドのディレクトリ構成 ● リアーキテクチャによりフロントエンドのレポジトリでモノレポ構成変更 ○ @hireroo/app - Web Application ■ クライアントライブラリにurqlを利用 ○ @hireroo/backend - BFF層 ○ @hireroo/graphql ■ GraphQLのSchema, Query, Mutationを定義 @hireroo/app import * as Graphql from “@hireroo/graphql/client/urql” @hireroo/graphql @hireroo/backend import type * as Graphql from “@hireroo/graphql/server/types” ディレクトリ間の依存関係 © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 38
コードの自動生成 ● @hireroo/graphql 定義したGraphQLのSchema ○ Client ■ Clientで使用するTypeScriptの型やqueryなどをgraphql-code-generatorを使って型情報を生成 ○ Server(BFF) ■ Backend For Frontendで使用するTypeScriptの型をgraphql-code-generatorを使って自動生成 ● Backend For FrontendではMicroserviceに通信を送るため、grpc clientを利用 ○ Protocol bufferの情報を利用して型安全なgrpc clientを自動生成 © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 39
GraphQLとは? ● ● ● Schema ○ Query ○ 参照 Mutation ○ 更新 © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 40
フロントとバックエンドの境界線における課題 よくある課題② ● ● ユーザー体験のペインポイントがどこにあるのか、分析を横断的に行いたいけど、 既存の保守や新規機能開発などで、分析用のログ取得やデータ基盤を整えるリソースがない そういった課題あるのではないでしょうか? ● HireRooでも似たような課題があります © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 41
フロントとバックエンドの境界線における課題 HireRooにおける課題② ● ● 将来的に各Microserviceのイベント情報を取得し、データ分析に活用したい 依存関係がある密結合になってしまっているMicroserviceのイベント情報を取得するとデータが重複してしまう イベント発生! イベント発生したタイミングで MicroserviceBとMicroserviceCのデータを含 んだMicroserviceAのデータを出力 Microservice B イベント発生! イベント発生したタイミングで MicroserviceBのデータを出力 Microservice A MicroService C データに重複ができてしまう →データ基盤を作る前に解決しておきたい! © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 42
課題解決に向けたリアーキテクチャ Hirerooのリアーキテクチャ後の境界線 Backend MicroService B MicroService D MicroService C MicroService E MicroService A Backend For Frontend Page 1 Page 2 Page 3 Frontend User © HireRoo, Inc. GraphQLを活用した Backend for Frontend へリアーキテクチャした話 43