🎉
LaravelでControllerをスリムにする!UseCase + DTOで始めるDDD設計入門
この記事では、UseCaseとDTO(データ転送オブジェクト)を使って、Controllerをスッキリさせる方法を書いていきます。
🔥 Before:バリデーションから保存まで全部コントローラ
public function store(Request $request)
{
$data = $request->validate([
'quote' => 'required|string|max:255',
'response' => 'required|string|max:10000',
]);
$reflection = auth()->user()->reflections()->create($data);
return response()->json($reflection, 201);
}
- バリデーション
- 永続化
- ログインユーザーの扱い
全部が Controller に書かれていて、責務が重くなってる状態です。
✅ After:UseCase / DTO に責務を分離!
📄 ReflectionData(DTO)
class ReflectionData
{
public function __construct(
public string $quote,
public string $response
) {}
}
📄 StoreReflectionRequest
public function toDto(): ReflectionData
{
return new ReflectionData(
$this->input('quote'),
$this->input('response'),
);
}
📄 CreateReflectionUseCase
public function handle(User $user, ReflectionData $data): Reflection
{
return $user->reflections()->create([
'quote' => $data->quote,
'response' => $data->response,
]);
}
📄 ReflectionController
public function store(StoreReflectionRequest $request, CreateReflectionUseCase $useCase)
{
$reflection = $useCase->handle(auth()->user(), $request->toDto());
return response()->json($reflection, 201);
}
🧠 メリット
| Before | After |
|---|---|
| バリデーションと保存が同居 | 役割を分離(RequestとUseCase) |
$request->validate() が Controllerに直接ある |
toDto() で型安全なデータを渡す |
| Controllerが肥大化しやすい | Controllerは「APIの入り口」としてスリムに |
✨ 応用:更新・削除もUseCaseへ
更新・削除も同様に UpdateReflectionUseCase、DeleteReflectionUseCase に分離できます。
public function update(UpdateReflectionRequest $request, Reflection $reflection, UpdateReflectionUseCase $useCase)
{
$updated = $useCase->handle(auth()->user(), $reflection, $request->toDto());
return response()->json($updated);
}
📦 ディレクトリ構成
app/
├── UseCases/
│ └── Reflection/
├── Dto/
│ └── ReflectionData.php
├── Http/Requests/
│ └── StoreReflectionRequest.php
│ └── UpdateReflectionRequest.php
✅ まとめ
- LaravelでもUseCase/DTOを導入すれば、Controllerをスリム化&テストしやすくできる
- テストを書きやすい
- 将来的にPolicy、Resource、Repositoryなどへも拡張しやすい
✍️ おまけ:次のステップ
- Policy導入で認可もUseCaseから分離
- Presenter / Resource 導入でレスポンス整形
- Repositoryパターン導入でDB層の切り分け
Discussion
UseCaseクラスの使い方が自分と全く同じでビックリしました。
毎回UseCaseクラスを手動で作るのが面倒なので、usecaseコマンドを登録し、
を叩くと、以下のインターフェースを実装した 命名+Action クラスを生成するようにしています。
登録しているコマンド
ありがとうございます!
これはすごい工夫ですね!
ユースケースごとに作成するとファイル数も増えてきますので、こうしてテンプレート化すれば確認するところも省略できます。
勉強になります!ありがとうございます!