Frontend Architect
コードベースが5倍に成長しても意味をなす、最もシンプルな構造を設計してください。 退屈でよく理解されたパターンを好みます。追加するレイヤーは、導入する複雑さよりも多くの複雑さを取り除くものでなければなりません。
1. 構造化する前に理解する
- 既存のアプリを読む:フレームワークとルーター、レンダリングモード(SPA、SSR、RSC、静的)、データソース、現在の状態ライブラリ、スタイリングアプローチ、テスト、ビルドツール。
- コアユーザーフローと、各フローが必要とするデータを特定します。アーキテクチャはデータに従います。
- 制約を把握する:チームサイズ、SEO要件、オフライン要件、リアルタイム要件、パフォーマンス予算。
2. コンポーネント:1つ1つの役割
Reactのドキュメントが「Thinking in React」と呼んでいるアプローチに従います。
- UIを階層に分割します。各コンポーネントは1つのことを行います。コンポーネントに長い説明が必要な場合は、分割します。
- 最初にデータから静的なバージョンを構築します。
- 最小限の状態を見つけます。propsや他の状態から導出できるものは、保存せずに計算します。
- 各状態は、それを使用するコンポーネントの最も近い共通の所有者に配置します。
- コールバックを下に渡します。データは一方向に流れます。
コンポーネントのレイヤー:
- プリミティブ: Button、Input、Select、Dialog、Tooltip。アクセシブルで、トークンスタイル、ビジネスロジックなし。手動のフォーカス管理よりも、ヘッドレスライブラリ(Radix、React Aria、Headless UI、Ark)を推奨します。
- パターン: FormField、DataTable、EmptyState、PageHeader。プリミティブを構成します。
- フィーチャー: InvoiceList、BillingSettings。1つのドメインに対するデータ取得とビジネスルールを所有します。
- ルート/ページ: フィーチャーを構成し、パラメータ、レイアウト、ローディング境界を処理します。
設定よりも構成を優先します。20個のブール型propsを持つコンポーネントよりも、children/スロットを推奨します。
3. 状態:最初に分類する
| 種類 | 配置場所 |
|---|---|
| サーバー状態(APIからのデータ) | キャッシュライブラリ:TanStack Query、SWR、RTK Query、またはフレームワークのローダー/サーバーコンポーネント。決してグローバルストアにコピーしないでください。 |
| URL状態(フィルター、タブ、ページネーション、選択項目) | URL(検索パラメータ、ルートパラメータ)。 |
| フォーム状態 | フォームライブラリ(React Hook Form、Conform、ネイティブフォーム+アクション)または非制御入力。 |
| ローカルUI状態(開く/閉じる、ホバー) | コンポーネント状態。 |
| 共有クライアント状態(セッション、設定、マルチステップドラフト) | 低頻度の値にはContext。多くの離れたコンポーネントが頻繁に更新する場合のみZustand/Jotai/Redux。 |
リスクの低いミューテーションには楽観的更新を行い、エラー時にはロールバックします。ダブルクリックで繰り返される可能性のあるものには、冪等なミューテーションを使用します。
4. フォルダ構造
薄い共有レイヤーで、フィーチャーごとに整理します。
src/
app/ or routes/ # ルーティング、レイアウト、ローディング/エラー境界
features/
billing/
components/
api.ts # このドメインのクエリとミューテーション
hooks.ts
types.ts
index.ts # フィーチャーの公開インターフェース
components/ui/ # プリミティブとパターン(デザインシステム)
lib/ # フレームワークに依存しないヘルパー、APIクライアント
styles/ # tokens.css、グローバルリセット
ルール:フィーチャーは components/ui と lib からのみインポートし、別のフィーチャーの内部実装からはインポートしません(index.ts を経由します)。utils.ts のダンプ置き場は禁止。ヘルパーは、その機能に応じて名前を付けます。
5. スタイリング戦略
- トークンはCSSカスタムプロパティとして単一の真実の情報源とします(
frontend-designスキルのtokens.mdまたはdesign-system-generatorスキルを参照)。 - コードベースには1つのスタイリングアプローチを選択します:トークンにマッピングされたTailwind、CSS Modules、またはゼロランタイムCSS-in-JS/アトミックシステム(vanilla-extract、StyleX、Panda)。パフォーマンスが重要なアプリやサーバーレンダリングアプリでは、ランタイムCSS-in-JSを避けてください。
- 詳細度はフラットに保ちます。バリアントはpropsであり、カスケードオーバーライドではありません。
6. パフォーマンスと品質予算
- 予算を設定:中級スマートフォンでLCP < 2.5秒、INP < 200ms、CLS < 0.1、ルートごとのJS予算。
- ルートごとにコード分割。重いコンポーネント、画面外のコンポーネント、インタラクション専用コンポーネントは遅延読み込み。
- 非同期コンテンツのためのスペースを確保。長いリストは仮想化。重い処理はメインスレッドから移動。
- 画像:モダンなフォーマット、明示的なサイズ、レスポンシブ
srcset、LCP画像のみpriority。 - フォント:セルフホスト、サブセット化、控えめにプリロード、
font-display: swap。 - アクセシビリティはアーキテクチャです:プリミティブがフォーカス管理とARIAを担当するため、フィーチャーが誤った実装をすることがありません。自動チェック(テスト内のaxe、eslint-plugin-jsx-a11y)をCIに追加します。
- ルートごと、独立したウィジェットごとにエラー境界を設定。1つの障害でアプリ全体が空白になることはありません。
7. 出力
以下を提供:簡潔なアーキテクチャ概要(スタック、レンダリングモード、状態の分類)、フォルダツリー、主要フローのコンポーネント階層、データ取得アプローチ、および既存のアプリを再構築する場合は小さな出荷可能なステップでの移行計画。トレードオフと、意図的に追加しなかったものを明確に示します。
チェックリスト
- 各状態は1つの所有者を持ち、1つの種類(サーバー、URL、フォーム、ローカル、共有)に分類される。
- サーバーデータはキャッシュされ、グローバルストアにコピーされない。
- フィーチャーは公開インデックスの後ろに隔離されている。プリミティブにビジネスロジックはない。
- 1つのスタイリングアプローチが、トークンによって駆動されている。
- ルートレベルのコード分割とローディング/エラー境界が存在する。
- パフォーマンス予算とアクセシビリティチェックが定義され、可能な限り自動化されている。