Laravelでクリーンアーキテクチャを実装する方法

1. はじめに

クリーンアーキテクチャとは?

近年、ソフトウェア開発では「保守性」や「拡張性」が重要視されています。特に、大規模なプロジェクトや長期運用が求められるシステムでは、「コードがスパゲッティ化してしまい、修正が大変…」「ちょっとした変更で他の部分に影響が出てしまう…」といった課題が発生しやすくなります。

そこで登場するのが「クリーンアーキテクチャ」です。これは、ロバート・C・マーティン(通称 Uncle Bob)が提唱したソフトウェア設計の考え方で、以下のような特徴を持っています。

  • ビジネスロジックを外部要因(DB・フレームワークなど)に依存させない
  • 依存関係を内側 → 外側に一方向にする(依存逆転の原則)
  • ドメイン(業務ルール)を第一に考える設計

Laravelの標準MVCとの違い

Laravelでは一般的に MVC(Model-View-Controller) のアーキテクチャが採用されています。
MVCはシンプルで直感的ですが、次のような問題点があります。

  1. ビジネスロジックがコントローラーやモデルに分散しやすい
  2. 変更があると、他の部分に影響を与えやすい
  3. データベースやフレームワークの仕様に依存しがち

クリーンアーキテクチャを取り入れることで、これらの問題を解決し、より柔軟で保守しやすい設計を実現できます。

この記事で学べること

この記事では、クリーンアーキテクチャの基本概念を説明しながら、Laravelに適用する方法を詳しく解説します。具体的には以下の内容をカバーします。

✅ クリーンアーキテクチャのレイヤー構造を理解する
✅ Laravelのフォルダ構成をクリーンアーキテクチャに最適化する
✅ 実際に「ユーザー登録」機能をクリーンアーキテクチャで実装する

この記事を読むことで、Laravelでクリーンアーキテクチャを実践できるようになり、より保守性が高く、拡張しやすいアプリケーション設計ができるようになります!💡

2. クリーンアーキテクチャの基本概念

クリーンアーキテクチャの4つのレイヤー

クリーンアーキテクチャは、コードの保守性と拡張性を高めるために、以下の4つのレイヤーに分かれています。

  1. エンティティ(Entities)
    • アプリケーションの 最も本質的なビジネスルール を定義する。
    • データベースやフレームワークに依存しない 純粋なPHPクラス で記述。
    • 例:User エンティティ(ID、名前、メールアドレスなどを持つ)
  2. ユースケース(Use Cases)
    • アプリケーションの ビジネスロジック(操作・処理) を実装。
    • エンティティを操作し、データのやり取りを制御。
    • 例:ユーザー登録やログイン処理
  3. インターフェース(Interface Adapters)
    • ユースケースと外部のデータ(リポジトリ、API、フレームワーク)をつなぐ役割。
    • コントローラー、リクエスト、レスポンス、リポジトリのインターフェースを含む。
    • 例:Laravelのコントローラー、リクエストクラス
  4. フレームワーク・データベース(Frameworks & Drivers)
    • フレームワーク(Laravel)やデータベースとの接続を担当。
    • LaravelのEloquent ORMやルーティング、コントローラーなどがここに含まれる。

✅ ポイント:依存関係のルール
クリーンアーキテクチャでは、外側のレイヤーが内側のレイヤーに依存する ルールになっています。
逆に、内側のレイヤー(エンティティやユースケース)は、フレームワークやデータベースの影響を受けません。

LaravelのMVCとの違い

Laravelの一般的なMVCアーキテクチャとクリーンアーキテクチャの違いを整理すると、以下のようになります。

項目LaravelのMVCクリーンアーキテクチャ
ビジネスロジックの場所コントローラーやモデルに分散ユースケースに集約
データベース依存Eloquent(ORM)に依存しがちリポジトリパターンで切り離す
コードのテストのしやすさ依存関係が強く、テストが難しいユースケース単位でテストしやすい
拡張性機能追加時に影響範囲が大きい影響範囲が限定され、保守しやすい

例えば、LaravelのMVCでは「ユーザー登録」の処理が コントローラーやモデルに散らばりがち ですが、クリーンアーキテクチャでは ユースケース層にまとめる ことで、コードの見通しがよくなります。

まとめ

クリーンアーキテクチャは「ビジネスロジックを独立させる」ことが最大の特徴です。
これにより、フレームワークに依存せず、長期的に保守しやすい設計 を実現できます。

3. Laravelでのフォルダ構成

クリーンアーキテクチャをLaravelに適用するためには、フォルダ構成 を適切に設計することが重要です。
ここでは、標準のLaravelの構成と、クリーンアーキテクチャを適用した構成を比較しながら解説します。

Laravelの標準フォルダ構成(MVC)

まず、Laravelの標準的なMVC構成は以下のようになっています。

app/
├── Http/
│   ├── Controllers/  ← コントローラー
│   ├── Requests/     ← バリデーション用リクエスト
│   └── Middleware/   ← ミドルウェア
├── Models/           ← Eloquentモデル
├── Providers/        ← サービスプロバイダ
database/
├── migrations/       ← マイグレーション
├── seeders/         ← データのシーダー
routes/
├── web.php           ← ルーティング定義

この構成では、ビジネスロジックがコントローラーやモデルに分散しがち で、プロジェクトが大きくなると保守が難しくなります。

クリーンアーキテクチャ適用後のフォルダ構成

クリーンアーキテクチャを適用すると、フォルダ構成は以下のようになります。

app/
├── Domain/           # エンティティ、リポジトリインターフェース
│   ├── Entities/
│   ├── Repositories/
├── UseCase/          # ビジネスロジック(ユースケース)
│   ├── User/
│   │   ├── RegisterUserUseCase.php
│   │   ├── UpdateUserUseCase.php
├── Infrastructure/   # データベースなどの実装
│   ├── Repositories/
│   │   ├── UserRepository.php
│   ├── Persistence/  # データベースとのやりとり
│   ├── API/          # 外部APIとの接続
├── Interface/        # コントローラー、リクエスト、レスポンス
│   ├── Controllers/
│   ├── Requests/
│   ├── Presenters/   # データの整形(レスポンス)

各フォルダの役割

フォルダ名役割
Domain/アプリのビジネスルール(エンティティ、リポジトリインターフェース)
UseCase/アプリケーションのユースケース(ユーザー登録、更新などの処理)
Infrastructure/データベースや外部APIとの接続(リポジトリ実装)
Interface/コントローラー、リクエスト、レスポンス(外部との接続)

Laravel標準構成との違い

  • ビジネスロジックがユースケース層に集約(コントローラーがシンプルになる)
  • データベースの実装がリポジトリ層に分離(Eloquentに依存しない設計が可能)
  • リクエストとレスポンスの整形をPresenterで統一(データの一貫性が保てる)

まとめ

このフォルダ構成を採用することで、Laravelのフレームワークに依存しない設計になり、テストしやすく、拡張性の高いアプリケーション を構築できます。

4. 実装例:シンプルな「ユーザー登録」機能を作る

ここからは、Laravelでクリーンアーキテクチャを適用しながら、「ユーザー登録」機能を実装していきます。
クリーンアーキテクチャのレイヤーごとにコードを分け、保守性の高い設計 にするのがポイントです!

4.1 エンティティ(Domain)

まずは、ユーザーのエンティティを定義します。
エンティティは、アプリケーションの本質的なデータ構造とルール を表します。

📂 app/Domain/Entities/User.php

namespace App\Domain\Entities;

class User
{
    private string $name;
    private string $email;
    private string $password;

    public function __construct(string $name, string $email, string $password)
    {
        $this->name = $name;
        $this->email = $email;
        $this->password = $password;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function getPassword(): string
    {
        return $this->password;
    }
}

ポイント

  • User エンティティは、データの持ち方を定義するだけ で、Eloquent(ORM)には依存しません。
  • Laravelの User モデルとは別に、ドメインのエンティティ を用意します。

4.2 リポジトリインターフェース(Domain)

次に、ユーザーのデータを保存・取得するためのインターフェース を定義します。
データの取得方法はここでは決めず、後でリポジトリクラス(実装)を作成します。

📂 app/Domain/Repositories/UserRepositoryInterface.php

namespace App\Domain\Repositories;

use App\Domain\Entities\User;

interface UserRepositoryInterface
{
    public function save(User $user): void;
}

ポイント

  • EloquentやDBに依存しない設計 にするため、インターフェースを作成。
  • ユースケース層は、この UserRepositoryInterface を通じてデータを操作します。

4.3 ユースケース(UseCase)

次に、ユーザー登録のビジネスロジック をユースケース層に実装します。

📂 app/UseCase/User/RegisterUserUseCase.php

namespace App\UseCase\User;

use App\Domain\Entities\User;
use App\Domain\Repositories\UserRepositoryInterface;

class RegisterUserUseCase
{
    private UserRepositoryInterface $userRepository;

    public function __construct(UserRepositoryInterface $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function execute(string $name, string $email, string $password): void
    {
        $hashedPassword = password_hash($password, PASSWORD_BCRYPT);
        $user = new User($name, $email, $hashedPassword);

        $this->userRepository->save($user);
    }
}

ポイント

  • ユーザーのパスワードをハッシュ化し、エンティティを作成。
  • UserRepositoryInterface を経由してデータを保存。(Eloquentは未使用)
  • フレームワークに依存しない設計 なので、どんな環境でも動作可能!

4.4 リポジトリの実装(Infrastructure)

次に、データベースと接続するリポジトリの実装 を行います。
ここで初めて Eloquent を使用 します。

📂 app/Infrastructure/Repositories/UserRepository.php

namespace App\Infrastructure\Repositories;

use App\Domain\Entities\User as DomainUser;
use App\Domain\Repositories\UserRepositoryInterface;
use App\Models\User as EloquentUser;

class UserRepository implements UserRepositoryInterface
{
    public function save(DomainUser $user): void
    {
        EloquentUser::create([
            'name' => $user->getName(),
            'email' => $user->getEmail(),
            'password' => $user->getPassword(),
        ]);
    }
}

ポイント

  • クリーンアーキテクチャでは、リポジトリがEloquentをカプセル化 する。
  • ユースケース層はEloquentの存在を知らない ので、Laravel以外のDBにも対応可能。

4.5 コントローラー(Interface)

次に、ユーザー登録リクエストを受け取るコントローラー を実装します。

📂 app/Interface/Controllers/UserController.php

namespace App\Interface\Controllers;

use App\UseCase\User\RegisterUserUseCase;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class UserController
{
    private RegisterUserUseCase $registerUserUseCase;

    public function __construct(RegisterUserUseCase $registerUserUseCase)
    {
        $this->registerUserUseCase = $registerUserUseCase;
    }

    public function register(Request $request): JsonResponse
    {
        $this->registerUserUseCase->execute(
            $request->input('name'),
            $request->input('email'),
            $request->input('password')
        );

        return response()->json(['message' => 'User registered successfully']);
    }
}

ポイント

  • コントローラーはユースケースを呼び出すだけ で、ビジネスロジックは書かない。
  • ユースケースを使うことで、リクエストの流れが明確になる

4.6 ルーティングと動作確認

📂 routes/web.php にルートを追加します。

use App\Interface\Controllers\UserController;
use Illuminate\Support\Facades\Route;

Route::post('/register', [UserController::class, 'register']);

📌 動作確認(APIリクエスト)

curl -X POST http://localhost/register \
     -H "Content-Type: application/json" \
     -d '{"name": "John Doe", "email": "john@example.com", "password": "secret"}'

✅ これで、クリーンアーキテクチャで実装したユーザー登録機能が動作 する! 🎉


まとめ

  • クリーンアーキテクチャを適用すると、ビジネスロジックが明確に分離 され、保守性が向上。
  • UserRepositoryInterface を使うことで、Eloquentに依存しない設計 が可能。
  • ユースケース層がデータフローを統制 し、拡張性の高いアプリケーションが作れる!

5. まとめ

この記事では、Laravelにクリーンアーキテクチャを適用する方法を学びました。
ここで、重要なポイントを振り返ります。

✅ クリーンアーキテクチャのメリット

  1. ビジネスロジックを分離できる
    • コントローラーやモデルにロジックを詰め込まないため、コードの見通しが良くなる。
  2. フレームワークやデータベースに依存しない設計ができる
    • ユースケース層が独立しているため、Laravel以外のフレームワークにも移行しやすい。
  3. テストがしやすい
    • ユースケースを単体テストできるので、データベースを使わなくてもロジックの検証が可能。
  4. 機能追加や変更に強い
    • 影響範囲が明確なので、新機能を追加しやすく、既存コードへの影響を最小限に抑えられる。

✅ LaravelのMVCとの違い

項目LaravelのMVCクリーンアーキテクチャ
ビジネスロジックの場所コントローラーやモデルに分散ユースケース層に集約
データの管理方法Eloquentモデルに依存リポジトリパターンを使用
コードのテストのしやすさEloquentが絡むとテストが難しいユースケース単位でテスト可能
保守性コードが肥大化しやすい各層が明確に分離されている

✅ 今後の応用

今回の「ユーザー登録機能」の実装をベースに、以下のような機能もクリーンアーキテクチャで実装できます。

  • ログイン機能(認証処理をユースケース層で管理)
  • パスワードリセット機能(ユースケース層でメール送信を制御)
  • ユーザー情報の更新・削除(リポジトリを使ったデータ管理)
  • 外部APIとの連携(Infrastructure層でAPIをラップする)

クリーンアーキテクチャを採用することで、大規模なアプリケーションでもスケールしやすく、保守性の高いコードを書くことができる ようになります!💡


📌 最後に

Laravelでクリーンアーキテクチャを適用するのは最初は難しく感じるかもしれませんが、実際にプロジェクトで取り入れることで、その効果を実感できるはずです。

ぜひ、実際のアプリ開発で試してみてください!🚀

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です