Service層って本当に必要?Laravelの肥大化したControllerを救う設計パターン

Laravelで開発を始めたばかりの皆さん、こんな経験はありませんか? 最初はわずか数行だったControllerが、機能を追加するごとにどんどん膨れ上がり、 気づけば数百行にも及ぶ「何でも屋」Controllerになってしまった…。

この状態は「Fat Controller(肥大化したコントローラ)」と呼ばれ、 初学者から熟練者まで多くの開発者が直面する典型的な課題です。

本記事では、この問題を解決する設計パターン「Service層」について、 必要性から 実装方法導入時の注意点 までを初心者にも分かりやすく解説していきます。


Service層は銀の弾丸か?その真の役割とは

Service層とは、ControllerとModelの間に入り、ビジネスロジックを担う層のことです。 つまり「アプリケーションのコアな処理」をControllerから切り離し、独立したクラスとして管理する設計手法です。

多くの初心者は、ユーザー登録や商品購入といった処理をすべてControllerに書いてしまいます。 しかしService層を導入すれば、Controllerは「交通整理役」に専念でき、可読性・保守性が大幅に向上します。

Service層が解決する主な課題
  • Controllerが肥大化し、コードが読みにくくなる
  • 同じ処理を複数のControllerに重複して書いてしまう
  • 再利用性が低く、単体テストが難しい

「Controllerはリクエストを受け取り、Serviceに処理を依頼し、結果を返すだけ」

これが本来あるべき姿です。

実践!Service層の作り方とControllerの軽量化

実際に「ユーザー登録機能」を題材に、Service層を導入してControllerをスリム化してみましょう。

悪い例 Controllerにロジックが詰まっている場合

// app/Http/Controllers/UserController.php
public function store(Request $request) {
    $data = $request->validate([...]);

    $user = new User();
    $user->name = $data['name'];
    ...
    $user->save();

    Mail::to($user->email)->send(new WelcomeEmail($user));
    Log::info('New user registered', ['id' => $user->id]);

    return redirect()->route('dashboard')->with('success', '登録完了しました。');
}

良い例 Service層でロジックを分離した場合

Serviceクラスにビジネスロジックを切り出すことで、Controllerは数行で済みます。

// app/Services/UserService.php
class UserService {
    public function registerUser(array $userData): User {
        $userData['password'] = Hash::make($userData['password']);
        $user = User::create($userData);
        Mail::to($user->email)->send(new WelcomeEmail($user));
        Log::info('New user registered', ['id' => $user->id]);
        return $user;
    }
}
// app/Http/Controllers/UserController.php
public function store(Request $request, UserService $userService) {
    $data = $request->validate([...]);
    $userService->registerUser($data);
    return redirect()->route('dashboard')->with('success', '登録完了しました。');
}

Controllerはわずか数行になり、読みやすさと再利用性が向上しました。


Service層とRepositoryパターンの違いと組み合わせ

よく混同されるのがRepositoryパターンです。 役割の違いを以下の表にまとめます。

役割
Service層 ビジネスロジックを担う
Repository層 データベース操作を抽象化する

Repositoryを導入すれば、データベース実装(EloquentやMongoDB)に依存しないコード設計が可能になります。 これにより、Service層はさらに純粋にビジネスロジックだけを扱えるようになります。もう少し詳しく書こうかなと考えたのですが、DI(依存性注入)などの話は少しむずかしいのでここまでにしておきます。そういうのがあるんだな程度で大丈夫です。


Service層だけがControllerを救うわけじゃない

Service層は強力ですが、他にもControllerを軽量化する設計手法があります。

Form Request
バリデーションをControllerから完全に分離できます。
Actionクラス
単一のアクションをクラス化することで、さらに細かく責務を分割可能です。

まとめ:Service層導入の注意点

Service層を導入すべきケース
  • Controllerが肥大化してきた時
  • 同じ処理が複数箇所で再利用される時
  • チーム開発で役割を分担したい時

一方で、小規模アプリで単純なCRUDだけの場合はオーバーエンジニアリングになる可能性もあります。 設計パターンは万能ではなく、プロジェクト規模や開発体制に合わせて取捨選択することが重要です。

最後に強調したいのは、Service層はControllerを救う有力な手段のひとつであるということです。 あなたのプロジェクトに取り入れることで、きっとコード品質と開発効率が向上するでしょう。