
はじめに:なぜドメイン駆動設計(DDD)が選ばれるのか?
ドメイン駆動設計(Domain-Driven Design)※以降DDD は聞いたことがある方が多いかと思いますが、そのメリットを実感するためには実際に使ってみることが一番かと思います。
今回、Laravelで構築している勤怠システムにおいて、完全な"ガチガチのDDD"ではなく、実務に合わせた簡易DDDを採用しているプロジェクトでしたので、そこで得られた知見を共有します。
勤怠システムというドメインの複雑さ
勤怠システムは想像以上に複雑な要素が多いです。
- 出勤・退勤・打刻
- 日次実績/月次集計
- 有給・特別休暇
- 残業/深夜/早朝計算
- 就業規則・締め日
- スケジュール/勤務パターン
さらに、
- 所属変更による規則切替
- 月またぎ計算
- 会社ごとに異なるルール
といった時系列で変化する依存関係が絡みます。
これを従来のMVC構造で実装すると、ControllerやModelにロジックが散在し、仕様変更のたびに修正箇所が広範囲に及ぶ問題が発生してしまいます。
従来設計の課題(Fat Controller問題)

よくある課題は以下です。
- Controllerにif文や計算ロジックが密集
- Eloquent Modelが肥大化
- Repositoryにビジネスロジック混入
- 条件分岐の増殖
- 影響範囲が読めない
これでは 仕様変更に弱く、運用コストの大きいシステムとなってしまいます。
実務で採用した簡易DDDの構成
完全なDDDではなく、以下のレイヤー分割を導入しました。
Controller
Request
UseCase
Domain Service
Domain Entity
Repository
Presenter
各レイヤーの責務
Controller
入出力のハブ。Request/UseCase/Presenterを呼び出すだけ。ほとんど処理は同じ。
Request
バリデーション専用。ここでは、型のチェックや文字数などフォーム入力の情報だけで簡易に弾けるものを検証する。
UseCase
業務フローの中心。Transaction管理。ここにビジネスルール等が記載される。
Domain Service
複雑ロジックや一意性チェックを集約。Requestでチェックできないドメインの深いビジネスルールが関連したバリデーションチェックも行われる。
Domain Entity
ドメインデータを保持(今回は振る舞い少なめ)。※本来はこのEntityはValueObjectを持つが今回はドメイン貧血構成のため未導入。
Repository
DBアクセスのみ。ロジック禁止。UseCaseやServiceとのやりとりはDomainEntityを介して。
Presenter
レスポンス整形専用。UseCaseの結果をレスポンス用のJsonに変換。
ドメイン駆動設計(DDD)を導入して得られたメリット
1. 責務の分離
「どこに何を書くか」が明確になりました。
レビュー時の指摘も構造ベースで統一できます。
2. テスト容易性
UseCase単位で業務フローを検証可能。
ControllerやViewに依存しません。
また、複雑なルールや判定ロジック等はServiceに切り出すため、ServiceのUnitTestで検証の領域を分離できる。
3. 可読性向上
ディレクトリ構造と責務が一致しているため、新規メンバーの理解が早くなりました。
4. 心理的安全性
仕様変更時の影響範囲が読みやすくなり、改修の不安が軽減しました。
実務で見えたDDDの課題
理想通りにはいきません。
ValueObject不足
プリミティブ型が多く、業務ルールがUseCaseに寄りがちでした。
Domain貧血
EntityがDTO化し、振る舞いを持たない設計になっています。
また、ValueObjectを持たないEntityは ActiveRecordとの境界が曖昧です。
Repositoryの非抽象化
Interfaceを持たないため、テスト時にDB依存が強い構造です。
なぜ「簡易DDD」は中途半端になりやすいのか
レイヤー分割を行っただけでは、ドメイン駆動設計(DDD)の本質である「ビジネスルールの集約」までは到達できません。
実際に運用してみると、
- UseCaseに条件分岐が増える
- Serviceが肥大化する
- Entityが単なるDTOになる
という状態が発生します。
つまり、
「構造はDDDだが、振る舞いは従来型」
というハイブリッドな状態です。
ここから一歩踏み込むには、
ValueObjectの導入や、Entityへの振る舞い移行が不可欠だと実感しました。
ガチガチのDDDでなくても効果はある
今回の実装は、戦略的DDDや集約設計まで踏み込んでいません。
それでも、
- レイヤー分離
- 責務明確化
- ロジックの集約意識
だけで、開発速度と保守性は大きく改善されたと感じております。
まとめ:実務に合わせたDDDが最適解
勤怠のような複雑なドメインでは、構造化は必須です。
必ずしも理想的なDDDを目指す必要はありません。
実務では、
- レイヤーを分ける
- ロジックの置き場所を決める
- ドメインの言葉でコードを書く
この3点だけでも十分効果があります。
今後は、
- ValueObjectの導入強化
- Entityへの振る舞い移行
- Repositoryの抽象化
を進めることで、よりドメイン駆動設計のメリットを活かしていきたいと考えています。