Share
Embed
機能的凝集の概念を用いて 複数ロール、類似の機能を多く含むシステムの フロントエンドのコンポーネントを適切に分割する
TSKaigi2025の登壇資料です
IkedaNoritaka
May 23, 2025
Transcript
54 slides
-
・NoritakaIkeda ・X(Twitter): @omotidaisukijp ・ROUTE06 Liam開発部 ・フロントエンド寄りのフルスタック ・TSKaigi2024ではLTで登壇 自己紹介 2 はじめに
機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
どっちが読みやすい? B 本発表では、 Bを 目指したいという話をし ます 5 厳密な正解がある わけではありません はじめに
機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する ロールごとにコンポーネント分割 -
今日話すこと 複数ロール、類似の機能を多く含む複雑なシステムの設計に、機能的凝集で立ち向かおう - フラグによる条件付きレンダリングを減らすことで、論理的凝集を回避する - 意味的に違う塊は別コンポーネントにする - 共通化できる塊は思っているより少ない 機能的凝集を行うことで、チームのベロシティ、プロダクト品質が向上した -
他のメンバーが書いたコード、自分が過去に書いたコードを読みやすい - 機能中心的に考える際、 APIのスキーマの構造、デザイン、要件を総合的にすり合わせて、各所へ フィードバックできる。プロダクトが本来目指すべき姿になっていく 6 はじめに 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
コンポーネント分割の観点の例 ドメイン・要件的な観点 - ロールごとの分割 - 機能ごとの意味的な分割 - 関連するデータを集約するための分割 UI的な観点 -
UIを構成する部品単位での分割 - UIライブラリのラッパーコンポーネント - レイアウト的に再利用可能なコンポーネント その他の観点 - フォームプロバイダーなど、技術的に必要な分割 7 はじめに 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
目次 - 凝集度とは : - 機能的凝集と論理的凝集 - なぜ機能的凝集を目指すべきなのか - 実際のプロジェクトで出会った機能的凝集パターン
: - Pattern1: ルーティングで分岐できる場合 - Pattern2: 分岐がコンポーネント内から排除出来ない場合 - Pattern3: 共通化したいなという気持ちが湧き出てきた場合 - Pattern4: コンポーネント分割単位との競合する場合 9 はじめに 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
凝集度とは モジュール(関数、クラス、パッケージなど)が単一の目的にどれだけ集中しているか、内部の要素(変数、関 数など)がどれだけ密接に関連しているかを表す指標 - 凝集度は高い、低いで表現される - 凝集度の高いモジュールは、堅牢性、信頼性、再利用性、可読性などの点で好ましい 偶発的凝集 論理的凝集 時間的凝集
手順的凝集 通信的凝集 逐次的凝集 機能的凝集 低い = 避けたい 高い = 好ましい 11 1. 凝集度とは 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
本スライドでは論理的凝集と機能的凝集に注目する 様々な凝集度の中でも、 UIやコンポーネント設計において頻出する、論理的凝集や機能的凝集に注目 - UI観点のコンポーネント設計では時間的凝集や手順的凝集など上記以外の他の凝集は生まれにくい 12 1. 凝集度とは 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する 偶発的凝集
論理的凝集 時間的凝集 手順的凝集 通信的凝集 逐次的凝集 機能的凝集 低い = 避けたい 高い = 好ましい -
論理的凝集とは 論理的凝集:似たような処理をフラグや条件で切り替えて 1つにまとめている状態(避けたい) - コンポーネントに条件分岐が多く点在するようになり、可読性が落ち、保守のコストが増加する - 共通化としてやりがち 13 1. 凝集度とは
機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する ロールごとの 機能を追加 商品詳細画面 ロールごとに条件分岐 -
なぜ論理的凝集を避けるのか コードが大きくなるにつれて、 条件分岐が増え、コードの複雑さが増していく 人間が書く文章 (要件定義書など )でも、 細部に場合分けが多い文章は読みにくいはず 「似ているから共通化したい」「今は簡単な 分岐だけだ」と思ったとしても、将来的に さらに条件を追加したくなるような場合は多い
17 1. 凝集度とは 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する 条件分岐がコードの中に点在 -
なぜ論理的凝集を避けるのか 18 1. 凝集度とは 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する 🚩開発初期の判断(ありがちな思考) : 「購入者と出品者で商品詳細の UI、ほとんど同じだし、 ひとつのProductDetailコンポーネントで共通化しよう」
「今は価格と在庫の表示を切り替えるだけだし、 role === 'buyer'の条件で分ければ十分」 🧨 時間が経ってから発生する現実 : 「購入者のモバイルの画面ではレビューを非表示にして」 「出品者の画面ではドラフト状態の時だけ公開ボタン出して」 「在庫がない商品は購入者にも出品者にも警告を出す。 ただしアドミンには出さない」 「管理者用にも同じコンポーネント使って、 IDと出品者情報表示し て」 「購入者がログインしてないときは購入ボタンを押せなくして」 条件分岐がコードの中に点在 -
なぜ機能的凝集を目指すのか ✨機能の修正や追加に耐えやすい - コードの修正が他の機能まで影響しにくい - 今修正したい、関心があるコードのみを読むことができる - 機能的凝集をしておけば、追加するコードも機能的凝集しやすくなる 🔧 要件とコードが一致しているか確認しやすい
- 要件定義書は機能単位で書かれているはずなので対応づけて読みやすい - 別の機能を開発しているメンバー、デザインエンジニアと意思疎通がとりやすい 19 1. 凝集度とは 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
登壇者の参加したプロジェクト背景 DXを推進したい分野の複雑な業務ドメイン - 複数ロール / 管理者ロール - ロールによって異なる業務フロー・状態遷移がある - ロール間で相互作用がある操作がある
- ロールごとに異なる操作権限・閲覧権限がある - 多岐にわたる業務フロー 前提 - フロントエンドチームは 5~7名程度 - 要件定義やデザイン、 APIスキーマは別チームが策定 - React + Vite + GraphQL(型付きAPIスキーマ) - 参考のADR: https://tech.route06.co.jp/entry/2023/08/08/115253 具体的なドメインは公開できないため、以後、 ECドメインを参考にサンプルコードを提示します 23 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する 2. 実際のプロジェクトで出会った機能的凝集パターン -
実際のプロジェクトで出会った、状況別、機能的凝集パターン 私たちのプロジェクトでよく出会ったり、設計的にトレードオフが強かった部分にフォーカスして、事例を共有し ます。それぞれの状況ごとに、私たちが行った判断を共有します。 - Pattern1: ルーティングで分岐できる場合 - Pattern2: 分岐がコンポーネント内から排除出来ない場合 -
Pattern3: 共通化したいなという気持ちが湧き出てきた場合 - Pattern4: コンポーネント分割単位との競合する場合 24 2. 実際のプロジェクトで出会った機能的凝集パターン 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
実際のプロジェクトで出会った、状況別、機能的凝集パターン - Pattern1: ルーティングで分岐できる場合 - Case1: ログインユーザのロールごとに類似画面を表示する - Case2: 新規作成画面と編集画面
- Pattern2: 分岐がコンポーネント内から排除出来ない場合 - Pattern3: 共通化したいなという気持ちが湧き出てきた場合 - Pattern4: コンポーネント分割単位との競合する場合 25 2. 実際のプロジェクトで出会った機能的凝集パターン 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
実際のプロジェクトで出会った、状況別、機能的凝集パターン - Pattern1: ルーティングで分岐できる場合 - Pattern2: 分岐がコンポーネント内から排除出来ない場合 - Case3: ディレクトリシステム
- Case4: 通知機能 - Pattern3: 共通化したいなという気持ちが湧き出てきた場合 - Pattern4: コンポーネント分割単位との競合する場合 31 2. 実際のプロジェクトで出会った機能的凝集パターン 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
Case3: ディレクトリシステム どこで条件分岐を行うか? - 出しわけの責務をコンポーネントの親になるコン ポーネントに移譲する - 出し分けるための責務を持ったコンポーネントを 作る -
もしくはList側でmapの中で出し分ける 36 Pattern2: 分岐がコンポーネント内から排除出来ない場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
Case4: 通知機能 通知はサイトを横断的にトリガーされるため、様々な UIデザインパターンが存在しやすい UI的に似ているが、以下の点で違いがある - 表示するラベルに入っているデータの種類・数 - 通知を押下して遷移する先の画面 37
Pattern2: 分岐がコンポーネント内から排除出来ない場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する 想定する通知の型 -
Case4: 通知機能 定義されている APIのスキーマ型を ts-patternで 出し分けると機能的凝集になりやすい .exhaustive()を用いることで、型の抜け漏れを防げる 追加開発のタイミングでも、特定の通知が飛ばない ようなエラーを未然に防げる 38
Pattern2: 分岐がコンポーネント内から排除出来ない場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
実際のプロジェクトで出会った、状況別、機能的凝集パターン - Pattern1: ルーティングで分岐できる場合 - Pattern2: 分岐がコンポーネント内から排除出来ない場合 - Pattern3: 共通化したいなという気持ちが湧き出てきた場合
- Pattern4: コンポーネント分割単位との競合する場合 39 2. 実際のプロジェクトで出会った機能的凝集パターン 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
Pattern3: 共通化したいなという気持ちが芽生えた時 共通化したいなと思う、エンジニア的な嗅覚・直感 - コードを局所的に見て、「ここの実装似ているかも?」「過去に似た実装をした記憶がある」「共通化した 先の幸せな未来が見える」 機能的な関連性を正しく評価したい - 要件、デザイン、 APIのスキーマの型からヒントを集め、総合的に判断する
- 要件、デザイン、 APIのスキーマは、実装がない中、様々な仮定のなかで作成されているものなので、そ れらの辻褄をしっかり合わせる作業になる 40 2. 実際のプロジェクトで出会った機能的凝集パターン 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
そもそも分割するに値するか、以下の要素から判断する 分割したい箇所の実装量、実装の複雑さ - ループ処理、状態管理、条件分岐などが入ってくると分割する価値があるかもしれない 分割したい箇所が、 Figmaでグルーピングもしくはコンポーネント化されているか - 共通化したいからといって、意味的に一塊のものから無理に切り出していないか 分割したい箇所が、 APIのスキーマのオブジェクト型の塊と一致しているか
- オブジェクト型でまとまっているものは一つの意味の塊として見出しやすい チーム内の (形式的/暗黙的な)合意に従う - コード規約 - 実際のコードの分割粒度 41 Pattern3: 共通化したいなという気持ちが芽生えた時 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
共通化できる時とはどんな時か (実装も、Figma上も似通っている前提 ) 要件定義的にも同じと言えるか - 購入者視点の「購入履歴画面」と、管理者視点の「注文管理画面」だと、意味的に同じだと言いにくい - その中のに通った UIを共通化していいか十分に注意を払うべき APIのスキーマのオブジェクト型的に同じかどうか
- Buyerオブジェクトの中の Userオブジェクトと、 SellerUserオブジェクトは同じだと言いにくい - 十分注意して共通化の判断をするか、 APIスキーマ側で Sellerオブジェクトの中の Userオブジェクトみたい な実装にできないか相談 42 Pattern3: 共通化したいなという気持ちが芽生えた時 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
(余裕があれば ) Case3: ディレクトリシステムの共通化の目処を立てる GoogleDriveみたいなシステム 47 Pattern2: 分岐がコンポーネント内から排除出来ない場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する でも、ここってデザイン的に、
1カラムでまとめないといけない気がする APIスキーマも、 nameとicon typeって並列に並んでいる →カラムのコンポーネントの中で出し分けるか -
(余裕があれば ) Case3: ディレクトリシステムの共通化の目処を立てる GoogleDriveみたいなシステム 48 Pattern2: 分岐がコンポーネント内から排除出来ない場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する レコード単位で
は、レコードを押した時の挙動と、ファイルに 対する操作を機能的分割したら良さそう レコードを押下した時の挙 動が違う ファイルに対する 操作がが違う ここは、組み合 わせのバリエー ションが多いの と使い回しの箇 所も多くないの で共通化しない でおくか -
実際のプロジェクトで出会った、状況別、機能的凝集パターン - Pattern1: ルーティングで分岐できる場合 - Pattern2: 分岐がコンポーネント内から排除出来ない場合 - Pattern3: 共通化したいなという気持ちが湧き出てきた場合
- Pattern4: コンポーネント分割単位との競合する場合 - Case5: 商品詳細画面 : 差分が小さい類似機能 49 2. 実際のプロジェクトで出会った機能的凝集パターン 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
Case5: 商品詳細画面 : 差分が小さい類似機能 差分が非常に小さい場合、責務がかなり薄いコンポーネントができてしまう - チームのコード規約にもよるが、責務が薄すぎるコンポーネントは可読性を落とすため、排除したい - BuyerProductPageは一行のUIを追加するためのコードと、 BigProductDetail
の重たい責務と並列に 存在する構造は、不自然でアンバランス 50 Pattern4: コンポーネント分割単位との競合する場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
Case5: 商品詳細画面 : 差分が小さい類似機能 ロール名や Typeのような意味的な命名のフラグではなく、機能を表示するかしないかの命名のフラグだと、分岐があっ ても機能的凝集に近い形にしやすい ただ大量にフラグを渡すと可読性を落とすので、小さい箇所で 1~2個が限度 51
Pattern4: コンポーネント分割単位との競合する場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
Case5: 商品詳細画面 : 差分が小さい類似機能 ReactNodeをプロップスとして渡す、もしくは React.children方法を渡す方法 UIのレイアウトの手法としてよく用いられる。 機能ごとの差分を親コンポーネントに集約したいときに使用。 子コンポーネント側だけを見て、何が渡されているのかわかりにくくなるため、使用 頻度は低い。命名はもっと具体的に、機能の名前にするべき。
52 Pattern4: コンポーネント分割単位との競合する場合 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
やってよかったこと チームのベロシティが安定した - 別のメンバーが担当していた機能のコードが読みやすく、誰でもどの機能の開発にもスムーズに入れる 状態 - pjの進め方として、タスクを細かくし、早く自分のタスクが終わった人は作業量が多い機能に入っていく 形式をとっていたので、マッチした コードと要件の対応がわかりやすいため、オンボーディングしやすい リリース前の
QAやテストなどでバグが報告された時、数ヶ月前に実装した自分のコードのキャッチアップ速度 が速くなる 53 まとめ 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する -
今日話すこと 複数ロール、類似の機能を多く含む複雑なシステムの設計に、機能的凝集で立ち向かおう - フラグによる条件付きレンダリングを減らすことで、論理的凝集を回避する - 意味的に違う塊は別コンポーネントにする - 共通化できる塊は思っているより少ない 機能的凝集を行うことで、チームのベロシティ、プロダクト品質が向上した -
他のメンバーが書いたコード、自分が過去に書いたコードを読みやすい - 機能中心的に考える際、 APIのスキーマの構造、デザイン、要件を総合的にすり合わせて、各所へ フィードバックできる。プロダクトが本来目指すべき姿になっていく 54 まとめ 機能的凝集の概念を用いて複数ロール、類似の機能を多く含むシステムのフロントエンドのコンポーネントを適切に分割する