LaravelでDDDをやるときの命名と配置の落とし所

2025.11.11 09:00
2025.11.12 11:30
LaravelでDDDをやるときの命名と配置の落とし所

LaravelでDDDをやろうとすると、「UseCase?Repository?どこに置けば?」と最初に迷います。
自分もいろいろ試して、ようやく“現実的に破綻しない落とし所”が見えてきました。

まず、分け方は4層+αくらいでちょうどいい

DDDをそのままLaravelに持ち込むと、抽象クラスやインターフェースだらけになって逆に見失いがちです。実務では、次のくらいの分け方に落ち着きました。

Domain         ← 業務のルール(エンティティ・例外・辞書)
Application    ← UseCase・DTO・Command/Result
Infrastructure ← DBや外部APIなどの実装
Presentation   ← Controller・Request・ViewModel
Support        ← Formatterや共通ヘルパ

Laravelのレイヤ構造を壊さず、役割ごとに責務を1つに絞るのがポイント。

命名は「動詞+名詞」でそろえる

「何をするか」が先、「何に対して」が後。
これでファイルツリーを見たときに、UseCaseが動作単位で並びます。

  • UseCase:SearchSpotUseCase, CreateSpotUseCase, PublishSpotUseCase
  • Repository:SpotRepository(IF)/DbSpotRepository(実装)
  • Query:SpotListQuery(IF)/DbSpotListQuery(実装)
  • 例外:SpotNotFoundException, NewsNotFoundException

呼び出す順番をそのまま読めるようにしておくと、プロジェクトが大きくなっても迷いません。

Repository と Query の使い分け

  • Repository … 「更新・削除・登録」など、ドメインの整合性を守る処理
  • Query … 画面表示や検索などの「読み取り専用・最適化された処理」

Eloquentを使うのはInfra層だけにして、
DomainからはEloquentを一切見ないようにしておくと長持ちします。

DTOとViewModelの語尾を決めておく

これを最初に決めておくと混乱しません。

役割命名
入力*Command / *QuerySearchSpotCommand
結果*ResultSpotListResult
*RowSpotListItemRow
画面表示用*ViewModelSpotListItemViewModel

ViewModelは整形を持たず、フォーマットは別クラス(DateFormatterなど)に任せます。

Controllerでは「3行で完結」できる構造に

final class SpotController extends Controller
{
    public function index(SpotSearchRequest $request, SearchSpotUseCase $uc)
    {
        $cmd = $request->toCommand();
        $result = $uc->handle($cmd);

        return view('spot.index', [
            'vm' => SpotListViewModel::fromResult($result),
        ]);
    }
}
  • Request:入力の正規化
  • UseCase:業務処理
  • ViewModel:表示用整形

この3点セットが自然に書ければ、設計はほぼ成功です。

例外はモジュールごとにハンドラを持たせる

共通の App\Exceptions\Handler を無理に拡張せず、
パッケージ内で GeneralExceptionHandler::register($handler) のように後付けする形が安全。
(詳しくは「Laravelでpackageを切るときの自作の例外ハンドラ」記事を参照)

実務で壊れにくいルール(最小)

  • 動詞+名詞+UseCase を統一
  • QueryとRepositoryを分ける
  • DTOは配列で返さない(型を守る)
  • EloquentはInfrastructureだけ
  • nullを返さず空値で処理する

この5つを守るだけで、チーム開発でもほとんど迷わなくなります。

まとめ

DDDを“完璧にやる”よりも、Laravelらしく分ける方が長持ちする。
命名と配置を最初に決め切っておくと、あとから人が増えてもスッと馴染みます。
迷ったら「UseCaseは動詞から」「EloquentはInfraだけ」——
この2つを思い出せば大体うまくいくかなと言う感じですね。

今回は以上です!