ディレクトリ構成の基本原則

Kodai Nakahara

2025.4.14

こんにちは。スリーシェイクの中原です。

プロジェクトが大きくなるにつれて「メンテナンスがしづらい」「開発スピードが遅い」と悩みを抱える要因の一つに「ディレクトリ構造がイケてない」があると考えています。

本日は、そういった悩みを解消するための、開発生産性を高めるためのディレクトリ構成について、私が考えていることをお話したいと思います。

背景

アプリケーション開発において、ディレクトリ構成はプロジェクトの保守性・可読性・開発効率に大きな影響を与えます。

適切なディレクトリ構成がなければ、以下のような問題が発生しやすくなります。

  • 責務が不明確になり、コードの依存関係が複雑化する
  • 新規参画者が理解しづらく、オンボーディングに時間がかかる
  • プロジェクトが成長するにつれて管理が破綻する
  • 変更の影響範囲が広がり、機能追加やバグ修正のコストが増大する

特に、長期的に運用されるプロジェクトや、チーム開発を前提とした案件では、適切なディレクトリ構成を設計することが成功のカギとなります。

目的

本記事では、ディレクトリ構成を設計する際に押さえておくべき5つの基本原則を解説します。

これらの原則を理解し、適用することで、以下のようなメリットを得られます。

  • チーム全体で統一感のある開発ができる
  • コードの見通しがよくなり、新規参画者がスムーズにキャッチアップできる
  • プロジェクトの成長に応じて、無理なく拡張できる
  • 変更の影響を最小限に抑えながら、柔軟に開発できる

これから紹介する「5つの基本原則」を意識することで、ディレクトリ構成をより良いものにしていきましょう。

5つの基本原則

ディレクトリ構成を設計する際には、以下の5つの基本原則を意識することが重要です。

原則概要
責務分離役割ごとにフォルダ・ファイルを整理し、責務を明確にする
スケーラビリティプロジェクトの成長に応じて、構成を無理なく拡張できるようにする
可読性と直感性新規参画者がすぐに理解できる、直感的なディレクトリ設計を心がける
再利用性共通ロジックを適切に分離し、効率的に再利用できるようにする
変更容易性影響範囲を最小限に抑えながら、新機能を追加しやすくする

責務分離

ディレクトリ構成を考える際、各フォルダ・ファイルが明確な役割を持ち、それぞれの責務が適切に分離されていることが重要です。

責務が曖昧な場合

  • 1つのファイルに複数の役割が混在し、修正が難しくなる
  • 変更の影響が予測しづらくなり、バグを生みやすい
  • どこに何のコードがあるのか分かりにくくなる

責務が分離されている場合

  • 役割ごとに整理されているため、コードの見通しがよくなる
  • 変更の影響範囲が限定され、保守しやすくなる
  • 再利用しやすいモジュール設計が可能になる

Bad 例

1つのファイルに複数の責務が混在

src/
├── app.ts             // 全ての処理をここに詰め込んでいる
├── user.ts            // UI・API・データ処理が混在
  • 表示・ロジック・データアクセスが1つの場所にあり、保守が困難
  • 役割が不明瞭で、他の人が見たときに理解しづらい

Good 例

役割ごとに整理

src/
├── controllers/       // ユーザーからの操作を受け付ける
│   └── userController.ts
├── services/          // ビジネスロジック
│   └── userService.ts
├── models/            // データ構造やDBアクセス
│   └── userModel.ts
  • 各レイヤーが明確な責務を持ち、単一目的で設計されている
  • 変更の影響範囲が限定される

スケーラビリティ

ディレクトリ構成は、プロジェクトの成長に柔軟に対応できるかという視点で設計することが大切です。

初期はシンプルで問題がなくても、機能追加・チームの拡大・運用期間の長期化などに伴い、構成が破綻するケースは少なくありません。

スケーラビリティが低い構成

  • 新しい機能を追加するたびに、既存構成を無理やり変更しなければならない
  • 同じ責務のファイルが複数の場所に分散し、全体像が見えにくくなる
  • 構成変更の影響範囲が大きく、保守コストが増加する

スケーラブルな構成

  • 機能ごとの追加や拡張がしやすい構造になっている
  • フォルダ構成に一貫性があり、新しい要素を自然に組み込める
  • チームや機能が増えても、構成の見通しが崩れにくい

特に、機能単位で責務が分かれているフィーチャーベース構成や、共通処理をモジュール化している構成は、スケーラビリティが高くなります。

Bad 例

成長を見越していない平面的な構成

src/
├── user.ts
├── product.ts
├── cart.ts
  • すべての処理がファイル単位で独立して増えていく
  • 新機能追加のたびに混沌としてくる

Good 例

レイヤー構成で将来も整理しやすい

src/
├── controllers/
│   ├── userController.ts
│   ├── productController.ts
├── services/
│   ├── userService.ts
│   ├── productService.ts
├── models/
│   ├── userModel.ts
│   ├── productModel.ts
  • 同じ構造で機能が増えても自然に拡張できる
  • 初期段階から導入可能なスケーラブルな形

可読性と直感性

誰が見ても理解しやすいディレクトリ構成であることは、チーム開発において非常に重要です。

可読性が低く、直感的でない構成

  • ディレクトリ名が曖昧で中身の想像がつかない(例: common, data, temp など)
  • 同じ責務のファイルが複数の場所に点在している
  • 初見のメンバーが「どこに何があるのか」迷いやすい

可読性・直感性が高い構成

  • ディレクトリやファイルの命名が明確で、一目で役割が分かる
  • フォルダ構成が機能や責務ごとに整理されており、初見でも迷いにくい
  • 開発者全員が構造を理解しやすいため、スムーズなオンボーディングが可能

“将来の自分や他人が見ても迷わない構成” を目指すことが、結果的にプロジェクト全体の効率を高めます。

Bad 例

曖昧な命名と意図不明な構成

src/
├── stuff/
├── temp/
├── misc.ts
  • 役割が名前から全く想像できない
  • どこに何があるのか分かりにくく、オンボーディングに時間がかかる

Good 例

誰が見てもわかる名前と構成

src/
├── controllers/
├── services/
├── models/
├── utils/
  • 命名だけでフォルダの目的が伝わる
  • 迷わず必要なコードにたどり着ける

再利用性

共通処理を適切に抽出して再利用できる構成は、開発スピードと品質の両方に貢献します。

再利用性が低い構成

  • 各機能で同じようなコードを何度も書いている
  • 共通処理が特定の機能に埋め込まれており、他で使いづらい
  • ロジックの重複により、修正ミスやバグの温床になりやすい

再利用性が高い構成

  • utils/, hooks/, components/, shared/ などの再利用可能な領域を明確に分離
  • 共通ロジックがモジュール化され、他の機能からも簡単に呼び出せる
  • DRY(Don’t Repeat Yourself)原則が徹底されているため、保守が楽になる

再利用性を意識した設計は、中長期的な開発コストの削減に直結します。

Bad 例

ロジックがあちこちに重複

// userController.ts
function formatDate(d: Date) { ... }

// orderController.ts
function formatDate(d: Date) { ... } // 同じ関数を再実装

共通ロジックが散らばり、修正時に全箇所対応が必要

Good 例

共通ロジックを utils にまとめて再利用

src/
├── utils/
│   └── format.ts
├── controllers/
│   └── userController.ts    // utils/format を使用
  • 同じ関数を1箇所で定義・管理できる
  • 再利用性が高く、バグの温床になりにくい

変更容易性

新機能追加や既存機能の修正がスムーズに行える構成も、保守性の高いプロジェクトには欠かせません。

変更しづらい構成

  • 1つの変更で複数の場所に影響が及ぶ
  • 機能の責務が入り混じっており、どこを修正すべきか判断が難しい
  • テストしにくく、影響範囲を特定しづらい

変更しやすい構成

  • 機能が疎結合で、影響範囲が局所的
  • モジュール単位でのテスト・修正がしやすい
  • 将来的な機能追加や仕様変更にも柔軟に対応可能

変更容易性は、開発フェーズだけでなく運用フェーズのストレス軽減にも大きく寄与します。

Bad 例

すべてが密結合で、ちょっとした変更が波及

// app.ts
handleUserLogin()
fetchUserData()
renderUserInfo()
// すべてが一体化されており、変更しづらい

関数同士が依存しすぎていて、1つの変更が他にも影響

Good 例

関心ごとが分離されており、局所的に変更可能

src/
├── controllers/
│   └── userController.ts     // ユーザー入力の処理
├── services/
│   └── userService.ts        // ロジック変更はここだけ
├── models/
│   └── userModel.ts          // データ構造の修正も限定的
  • 修正が局所的で済み、他ファイルへの影響が最小限
  • テストもしやすく、安心してリファクタできる

まとめ

アプリケーションのディレクトリ構成は、単なる「ファイルの置き場」ではありません。
それはチーム全体の開発効率、保守性、スケーラビリティに直結する設計の一部です。

本記事で紹介した「5つの基本原則」は、どの技術スタックでも応用可能な普遍的な考え方です。

💡 最初から完璧な構成を目指す必要はありません。

大事なのは、チームの成長・プロジェクトの変化に合わせて構成を見直し続ける姿勢です。

「なんとなく置いた構成」ではなく、意図を持った設計を心がけましょう。

それが結果として、開発のスピードと品質の両立につながります。

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ