ディレクトリ構成 — 規模と責務で決めるファイル配置
この章の目次開く
「このコンポーネント、どこに置けばいいんだろう」「あの処理を書いたファイル、どこだっけ」——ディレクトリ構成が設計されていないプロジェクトでは、この迷いが毎日発生します。
ディレクトリ構成はただの整理整頓ではありません。「どこに何があるか」の予測可能性と、「変更の影響がどこまで及ぶか」の見通しを決める、立派な設計対象です。この章では、代表的な2つの構成パターンと、規模に応じた使い分けを解説します。
学習者ディレクトリ構成って正解があるの?プロジェクトごとに全然違ってて、毎回どれが正しいのか分からなくなる…
先生唯一の正解はないけど、「分類の軸」は2つしかないんだ。技術の種類で分けるか、機能で分けるか。世の中の構成パターンはほぼ全部、この2軸の組み合わせで説明できる。軸を理解すれば、初見のプロジェクトでも構成の意図が読めるようになるよ。
パターン1: レイヤー別(技術別)構成
ファイルを「それが何であるか(技術的な種類)」で分類する構成です。小規模なプロジェクトやチュートリアルでよく見る形で、MVCフレームワークの伝統的な構成もこの系統です。
src/
├── components/ # UIコンポーネント全部
│ ├── UserCard.tsx
│ ├── OrderList.tsx
│ └── ProductDetail.tsx
├── hooks/ # カスタムフック全部
│ ├── useUser.ts
│ └── useOrders.ts
├── services/ # API呼び出し・ビジネスロジック全部
│ ├── userService.ts
│ └── orderService.ts
├── types/ # 型定義全部
│ └── index.ts
└── utils/ # 汎用関数全部
└── format.ts
レイヤー別の長所と短所
| 観点 | 評価 |
|---|---|
| 置き場所の判断 | 迷わない。「フックだから hooks/」と機械的に決まる |
| 小規模での見通し | 良い。ファイル数が少ないうちは全体を把握しやすい |
| 機能の変更 | つらい。1機能の修正で components/ hooks/ services/ を行き来する |
| 機能の削除 | つらい。関連ファイルが散らばっているため消し漏れが出る |
| ファイル数の増加 | components/ に50ファイル並ぶと、もはや分類の意味がない |
問題の本質は、実際の開発作業が「機能単位」で発生することです。「注文機能にキャンセルを追加する」という1つの変更が、レイヤー別構成では複数のディレクトリを横断します。
パターン2: 機能別(フィーチャー別)構成
ファイルを「どの機能のためのものか」で分類する構成です。
src/
├── features/
│ ├── user/
│ │ ├── components/UserCard.tsx
│ │ ├── hooks/useUser.ts
│ │ ├── api.ts
│ │ └── types.ts
│ ├── order/
│ │ ├── components/OrderList.tsx
│ │ ├── hooks/useOrders.ts
│ │ ├── api.ts
│ │ └── types.ts
│ └── product/
│ └── ...
└── shared/ # 複数の機能から使う共通部品
├── components/Button.tsx
└── utils/format.ts
この構成の背景にあるのが**コロケーション(colocation)**という考え方です。
一緒に変更されるファイルは、近くに置く——変更の単位とディレクトリの単位を一致させることで、修正・削除の影響範囲がフォルダ1つに収まります。「注文機能の修正」は features/order/ の中で完結し、「注文機能の廃止」は features/order/ の削除でほぼ終わります。レビューでも「このPRは features/order/ しか触っていないから、他の機能への影響はない」と判断できます。
機能別の長所と短所
| 観点 | 評価 |
|---|---|
| 機能の変更・削除 | 強い。影響範囲がフォルダ単位で閉じる |
| チーム分担 | 機能ごとに担当を分けやすく、コンフリクトも減る |
| 置き場所の判断 | 迷う場面がある。「複数の機能で使うものはどこに置く?」問題が発生する |
| 小規模での手間 | 数ページのアプリでは大げさ。フォルダの階層だけ深くなる |
どちらを選ぶか — 規模と変更の単位で決める
判断の目安は次のとおりです。
- レイヤー別でよいケース — 画面数が少ない、機能同士の境界が曖昧、プロトタイプや学習用。分類のオーバーヘッドをかける価値がまだない段階
- 機能別に移るサイン —
components/の中を「機能名で検索」して探すようになった、1つの修正で触るディレクトリが常に3つ以上ある、機能の削除で消し漏れが出た

なお、Next.jsのApp Routerのようにフレームワークがルーティング用のディレクトリ規約を持つ場合は、その規約(app/ 配下)はフレームワークに従い、それ以外のコード(features/、shared/ など)を自分たちの方針で設計する、という組み合わせになります。
依存の方向を設計する — 構成はimportルールとセットで
ディレクトリを分けただけでは、構成は簡単に崩れます。崩壊を防ぐ本体は、「どこからどこへのimportを許すか」というルールです。
機能別構成での典型的なルールは次の3つです。
⭕ 許可される依存
features/order → shared # 機能は共通部品を使ってよい
features/user → shared
❌ 禁止される依存
features/order → features/user # 機能同士の直接import禁止
shared → features/order # 共通部品が特定機能に依存するのは禁止
- feature同士は直接importしない —
orderがuserの内部ファイルに依存すると、userの変更がorderを壊すようになり、フォルダを分けた意味が消えます。複数の機能で必要になったものはsharedへ昇格させます - sharedはfeatureに依存しない — 依存の方向は常に
features → sharedの一方通行にします。これはSOLID原則で扱った「安定したものに依存する」という考え方のディレクトリ版です - feature内部への直接アクセスを制限する — 各featureの入り口となる
index.tsで公開するものを明示し、外部からはfeatures/order経由でのみimportさせる形にすると、機能の内部構造を自由に変更できます
よくあるハマりどころ
utils/ と common/ のゴミ箱化
「どこに置くか迷ったら utils/ へ」を繰り返すと、utils/ は分類不能なコードの吹き溜まりになります。対策はシンプルで、置くときに具体的な名前を付けることです。utils/format.ts より shared/date/formatDate.ts。「共通っぽいから」ではなく「2つ以上の機能で実際に使われているから」shared に置く、という基準も有効です。
学習者とりあえず utils に置いて、あとで整理しよう…っていつも思うんだけど、その「あとで」が来たことがない気がする。
先生みんなそう(笑)。だから「置くとき」が唯一の整理チャンスだと思った方がいい。迷ったら、そのファイルを使う側の機能のフォルダに置くのがおすすめ。共通化は2回目に使いたくなったときにやれば遅くないよ。
早すぎる共通化・早すぎる細分化
1つの機能でしか使っていないコンポーネントを、将来を見越して shared/ に置くと、「どの機能から使われているか分からないから変更が怖い」コードが増えます。また、ファイルが3つしかない段階で components/hooks/api/types/constants の5フォルダを切るのも、階層が深くなるだけです。構造は必要になってから増やす方が、結果的に綺麗に保てます。
「1ファイルが大きくなったら分割」の基準がない
ディレクトリ構成を決めても、1ファイルに全部書かれたら意味がありません。「コンポーネントとデータ取得ロジックは分ける」「1ファイルは目安〇行まで」のような分割の目安も、構成ルールとセットでREADMEなどに明文化しておくと、構成が長持ちします。
構成ルールがドキュメント化されていない
構成は「わかっている人」には自明でも、新しく入ったメンバーには見えません。どこに何を置くか・何が禁止かを README や CONTRIBUTING に数行でよいので書いておきます。これは伝わるコードとドキュメントで扱った「読者を意識したドキュメント」の実践でもあります。
ちゃんと使うためのポイント
- ディレクトリ構成の分類軸は「技術の種類」か「機能」の2つ。どちらで分けるかを意識的に選ぶ
- 小規模はレイヤー別で十分。変更が複数ディレクトリにまたがり始めたら機能別へ
- 機能別構成の核はコロケーション——一緒に変わるものを近くに置き、変更・削除をフォルダ単位で閉じる
- 構成はimportルール(features同士の直接import禁止、shared は features に依存しない)とセットで初めて機能する。ルールはESLintで強制する
utils/をゴミ箱にしない。迷ったら使う側の機能フォルダに置き、共通化は2回目から- 構成ルールは明文化する。新メンバーが「どこに置くか」を質問しなくて済む状態がゴール
次の章では、ディレクトリに配置する中身の側——実務で頻出するデザインパターン(Repository・Service・Factory)を解説します。
参考リンク
- Bulletproof React - GitHub — 機能別構成の代表的なリファレンス実装(英語)
- Feature-Sliced Design — フロントエンド向けの体系的なディレクトリ設計方法論(英語)
- Next.js: Project Structure — App Routerのディレクトリ規約公式ガイド(英語)