【Laravel】Laravelのディレクトリ構成をカスタマイズする考え方

laravelアイキャッチ Laravel

こんにちは、かつコーチです。

これまでService・Repositoryパターン、Form Requestと、責務ごとにクラスを分ける方法を紹介してきました。

クラスの数が増えてくると、次に気になるのが「これらをどこに置くか」というディレクトリ構成の問題です。

今回は、Laravelのデフォルト構成の限界と、カスタマイズする際の考え方を整理します。

デフォルトのディレクトリ構成の限界

app配下がControllerだらけになる問題

Laravelのデフォルト構成では、app/Http/Controllers にControllerが集中します。

そこにこれまで紹介してきたServiceやRepositoryを素朴に追加していくと、app 直下がこんな状態になりがちです。

app/
├── Http/
│   ├── Controllers/
│   │   ├── PostController.php
│   │   ├── CommentController.php
│   │   └── CategoryController.php
│   └── Requests/
│       ├── StorePostRequest.php
│       └── UpdatePostRequest.php
├── Services/
│   ├── PostService.php
│   └── CommentService.php
├── Repositories/
│   ├── PostRepositoryInterface.php
│   ├── EloquentPostRepository.php
│   └── ...
└── Models/
    ├── Post.php
    └── Comment.php

一見整理されているようですが、「投稿(Post)」に関する機能を探そうとすると、Controller・Request・Service・Repository・Modelの5つのフォルダを横断しなければなりません。

機能数が増えるほど、この「1つの機能を見るために複数フォルダを行き来する」コストが積み重なっていきます。

種類別か機能別か

これは「技術的な種類(レイヤー)でフォルダを分けるか」「機能(ドメイン)でフォルダを分けるか」という、設計の考え方の違いに起因します。

デフォルトのLaravel構成は前者、つまりレイヤーごとにまとめる考え方が土台になっています。

カスタマイズの考え方

レイヤーごとにまとめる(デフォルトに近い発展形)

小〜中規模のプロジェクトでは、デフォルトのレイヤー分けを維持しつつ、Service・Repositoryのフォルダを整えるだけでも十分です。

app/
├── Http/Controllers/
├── Services/
├── Repositories/
└── Models/

この構成のメリットは、Laravelの標準的なやり方に近く、他の開発者が見てもすぐに理解できる点です。

機能ごとにまとめる(Feature-based)

一方、機能数が多い中〜大規模プロジェクトでは、「機能(ドメイン)ごとにフォルダをまとめる」構成が有効になる場合があります。

app/
├── Domain/
│   ├── Post/
│   │   ├── PostController.php
│   │   ├── PostService.php
│   │   ├── PostRepositoryInterface.php
│   │   ├── EloquentPostRepository.php
│   │   └── Post.php
│   └── Comment/
│       ├── CommentController.php
│       ├── CommentService.php
│       └── Comment.php

「投稿に関するものはすべて Domain/Post を見ればいい」という状態になり、機能単位での見通しが良くなります。

判断軸をまとめると、次のようになります。

観点レイヤー別機能別(Domain)
学習コスト低い(標準的)やや高い(独自ルールの理解が必要)
機能追加時の見通し複数フォルダを横断1フォルダで完結しやすい
向いている規模小〜中規模中〜大規模、機能数が多い場合

実装例

Service/Repositoryフォルダの配置と名前空間

機能別に構成を変える場合、名前空間(namespace)もディレクトリ構成に合わせて変更する必要があります。

// app/Domain/Post/PostService.php
namespace App\Domain\Post;

use App\Domain\Post\PostRepositoryInterface;

class PostService
{
    public function __construct(
        private PostRepositoryInterface $postRepository
    ) {}

    // ...
}
// app/Domain/Post/PostController.php
namespace App\Domain\Post;

use App\Http\Controllers\Controller;
use App\Http\Requests\StorePostRequest;

class PostController extends Controller
{
    public function __construct(
        private PostService $postService
    ) {}

    // ...
}

名前空間をディレクトリ構成と一致させることで、PSR-4のオートロードが正しく機能します。

composer.jsonのautoload設定

app/ フォルダ自体はデフォルトで App\ 名前空間にマッピングされているため、app/Domain/Post/PostService.phpApp\Domain\Post\PostService として扱う分には、追加の設定は不要です。

ただし、app/ の外側(例えば src/ フォルダなど)に独自のディレクトリを作る場合は、composer.jsonautoload セクションを編集する必要があります。

{
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Domain\\": "src/Domain/"
        }
    }
}

編集後は、必ずオートローダーの再生成コマンドを実行します。

composer dump-autoload

つまずきポイント:namespaceとautoloadのズレでハマった話

かつコーチが実際につまずいた話

以前、既存プロジェクトのディレクトリ構成をレイヤー別から機能別に移行していたとき、原因不明の「Class not found」エラーに何度も遭遇しました。

❌ Before:ファイルを移動しただけでnamespaceを直していない

// app/Domain/Post/PostService.php に移動したのに、namespaceが古いまま
namespace App\Services; // ファイルの実際の場所と一致していない

class PostService
{
    // ...
}

ファイルだけを新しいフォルダに移動して、namespace 宣言の書き換えを忘れていました。

PHPのオートローダーは「クラス名(名前空間)」と「ファイルパス」が一致していることを前提に動いているため、ズレがあると Class "App\Domain\Post\PostService" not found のようなエラーになります。

さらに厄介だったのが、composer dump-autoload を実行し忘れていたことです。

Laravelは本番環境などでオートロードの結果をキャッシュしているため、ファイルを移動しただけではキャッシュが更新されず、しばらく古い場所を参照し続けてしまいました。

✅ After:ファイル移動時はnamespaceも同時に修正し、autoloadを再生成する

// app/Domain/Post/PostService.php
namespace App\Domain\Post; // ファイルの場所と一致させる

class PostService
{
    // ...
}
# ファイル移動・namespace変更のあとは必ず実行する
composer dump-autoload

「ファイルを移動したら、namespaceの修正とオートロードの再生成をセットで行う」という手順を徹底するようになってから、この種のエラーには遭遇しなくなりました。

応用:チームでの合意形成と過度なカスタマイズのリスク

チームでの合意形成が前提

ディレクトリ構成のカスタマイズは、一人のプロジェクトなら好きに決められますが、チーム開発では話が変わってきます。

Laravelの標準構成から大きく外れるほど、新しく参加したメンバーが「どこに何があるか」を理解するまでの学習コストが上がります。

導入する前には、必ずチーム内でメリット・デメリットを共有し、合意を取ってから進めるべきです。

過度なカスタマイズのリスク

「きれいな構成にしたい」という気持ちが先行しすぎると、抽象化のためのレイヤーばかりが増え、逆にコードを追いにくくなることがあります。

ディレクトリ構成の変更は、あくまで「今のプロジェクトの規模・チーム体制に対して適切かどうか」を基準に判断するのがおすすめです。

迷ったときは、まずデフォルトに近い構成から始め、実際に困ってから少しずつカスタマイズしていく、というくらいがちょうど良いバランスだと感じています。

まとめ

この記事のポイント

  • デフォルトのレイヤー別構成は、機能数が増えると1機能を追うのに複数フォルダを横断するコストが上がる
  • レイヤー別と機能別(Domain)、それぞれにメリット・デメリットがあり、プロジェクト規模で判断する
  • ファイルを移動する際は、namespaceの修正とcomposer dump-autoloadの実行をセットで行う
  • チーム開発では、構成変更前に必ずメンバー間で合意形成を行う

次に読むべき記事

ここまでで、Controller・Service・Repository・Form Request・ディレクトリ構成という設計の基礎が一通りそろいました。

次回からは、実際にLaravel×Vue(Inertia.js)を使って、簡単なTODOアプリを一緒に作っていきます。

→ 次の記事:Laravel×Vue/Inertiaで簡単なTODOアプリを作ってみる

タイトルとURLをコピーしました