Day 6・責務の分離とデータ整合性 — サービス層・トランザクション・排他制御
Fat Controller問題 — コントローラに何もかも書くと何が壊れるか
テスト困難・再利用不可・読めない。肥大化したコントローラの具体的な害を知る。
Fat Controller とは
とは、コントローラのメソッドにバリデーション・ビジネスロジック・DB操作・通知・外部API呼び出しまで、あらゆる処理を詰め込んで肥大化した状態を指します。最初は動くので気づきにくいのですが、機能が増えるほど手が付けられなくなります。
本来コントローラの責務は『HTTPリクエストを受け取り、適切な処理を呼び出し、レスポンスを返す』という交通整理だけです。判断や計算そのものを抱え込むと、責務が肥大化します。
肥大化したコントローラの例
public function store(Request $request)
{
$data = $request->validate([...]);
// 在庫チェック
$product = Product::find($data['product_id']);
if ($product->stock < $data['quantity']) {
return back()->withErrors('在庫不足');
}
// 金額計算(割引ロジックまでここに)
$amount = $product->price * $data['quantity'];
if ($request->user()->isPremium()) {
$amount *= 0.9;
}
// 在庫を減らして注文を作る
$product->decrement('stock', $data['quantity']);
$order = Order::create([...]);
// ポイント付与
$request->user()->increment('points', (int) ($amount * 0.01));
// メール送信
Mail::to($request->user())->send(new OrderCompleted($order));
return redirect()->route('orders.show', $order);
}何が悪いのか — 三つの害
- テストが困難: このロジックを検証するにはHTTPリクエストを丸ごと組み立てる必要があり、割引計算だけを単体で試せない。
- 再利用できない: 同じ注文処理をバッチ処理やAPIから呼びたくても、コントローラの中に埋まっていて呼び出せない。
- 読めない: 在庫・金額・ポイント・メールが一つのメソッドに混在し、どこで何が起きるか追いにくい。
なぜ『薄いコントローラ』を目指すのか
コントローラをHTTPの入出力だけに絞ると、中身のビジネスロジックをHTTPから切り離してテストでき、他の入口(バッチ・API・コマンド)からも再利用できます。読み手も『この画面は何を呼んでいるか』を一目で追えます。肥大化を放置すると、変更のたびに影響範囲が読めず、修正が怖くなるのが最大の問題です。
モデルに全部書くのも別の肥大化
『コントローラが薄ければモデルに全部書けばいい』とすると、今度はモデルが肥大化します(Fat Model)。複数のモデルをまたぐ業務手順は、次のレッスンのサービス層に置くのが自然です。
面接ではこう言う
「コントローラにロジックを詰め込むと、HTTPと業務処理が密結合して、単体テストも再利用もできなくなります。だからコントローラは受け取って呼んで返すだけに絞り、業務ロジックは別の層に切り出します。肥大化の一番の害は、変更の影響範囲が読めなくなって修正が怖くなることだと考えています」
読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。
理解度チェック
答えられれば、面接で説明できる状態に一歩近づきます。
Fat Controller(肥大化したコントローラ)の害として、当てはまらないものは?
コントローラにビジネスロジックを書くと、なぜテストが難しくなるのですか。
記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。