Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Laravel DDD データ詰め替え戦略

Avatar for okamos okamos
December 11, 2025
21

Laravel DDD データ詰め替え戦略

Avatar for okamos

okamos

December 11, 2025
Tweet

Transcript

  1. はじめに DDDで開発を行う際、レイヤー間で「データの詰め替え」が発生します。 Laravel特有ではなく一般的な問題であり、以下のようなマッピング方法が知られています。 " 引用: データ詰め替え戦略 1. No Mapping (レイヤ間でモデルを共有し、詰め替えをしない)

    2. 2-way Mapping (各レイヤで独自のモデルを持ち、レイヤを跨ぐ呼び出しは上位レイヤが詰め替え の責務を負う) 3. Full Mapping (各レイヤで独自のモデルを持ち、レイヤを跨ぐ呼び出しには専用のモデルを使う) ここはありがちなレイヤードアーキテクチャを仮定し の4層レイヤを考えます。 まずは “1. No Mapping” から考え、徐々に依存度を落としていきます。 アプリケーション層 データアクセス層 プレゼンテーション層 ドメイン層 ・ ・ ・
  2. No Mapping データアクセス層 No Mappingは、データアクセス層から見た場合、非常に都合の良い戦略です。 上位モジュールから渡されたインスタンスをそのまま永続化できるため、データの詰め替え 処理が不要になります。また、テーブル構造に変更があった場合でも、EloquentModel (DomainModel)が自動的に追従するため、デグレが起きにくいという利点があります。 アプリケーション層 データアクセス層に強く依存しているため、業務ロジックを実装する際にデータベースの知

    識が染み出してしまいます。 本来、アプリケーション層は純粋な業務ロジックのみを実装するレイヤですが、この構成では テーブル変更の影響を直接受けてしまいます。つまり、アプリケーション層はデータベースの 都合に振り回される脆弱なレイヤと言えます。 この結果、開発者は本来の責務である業務ロジックの実装に加えて、テーブル構造やリレー ションといった永続化の詳細まで考慮する必要が生じます。
  3. No Mapping これでいいのでは? 正直なところ問題ないです。 多くの場合、No Mapping の戦略はうまくいきます。 ただし、現在はプレセンテーション層までDomainModelを伝搬させている都合上、 どうしてもデータの整形や条件分岐をControllerもしくはview で行う必要があります。

    viewで表示ロジックを実装する例 これは少しテストが辛そうです。また表示に関するロジックが点在することになります。 解決するのはViewModel(Presenter) と呼ばれる手法です。
  4. ViewModel の導入 こんなことが本当に必要なのかどうかの議論は昔からなさているそうですが、少なくとも プレゼンテーション層においては有効に働きそうです。 ViewModelはSmartUIやFatControllerというMVCでの開発でしばしば遭遇する問題 に対する1つの回答です。 参考: Skinny Controller, Fat

    Model Rails にはActiveRecordをそのまま使いながら、viewで必要な表示ロジックだけを後付けで追加できるDraperとい う便利なツールがあります。ActiveRecordを委譲したDecoratorを実装するだけで利用可能です。 ! Tips " 引用: ローカルDTO Martin Fowler's Bliki (ja) DTOのようなものを使うとよいのは、 プレゼンテーション層のモデルとドメインモデルとの間に 大きなミスマッチが ある場合です。 この場合、プレゼンテーションに特化したファサード(またはゲートウェイ)を作り、 ドメインモデルを マッピングして、プレゼンテーションに都合のよいインターフェースを 提供するのは理に適っています。 これは Presentation Model とも合います。
  5. ViewModel導入で何が解決したか 解決した問題 まだ残っている問題 - プレゼンテーション層の責務が明確になった - 表示ロジックのテストが書きやすくなった - Fat Controller

    / Smart View を回避できた - アプリケーション層が データアクセス層に依存したまま - DomainModel = Eloquent Model という構造は変わらない - テーブル変更がアプリケーション層に波及する
  6. アプリケーション層の独立性 ViewModelは プレゼンテーション層 を「出口」で分離した。 では「入口」はどうでしょうか。 現状 Controller → UseCase(Eloquentに依存)→ Repository

    - Requestの構造がそのままUseCaseに流れ込む - HTTPリクエストの都合がビジネスロジックに影響する - UseCaseの単体テストにRequestのモックが必要 InputData(入力用DTO)を導入し、依存を切る 問題 解決策
  7. データアクセス層の分離 2-way Mappingで上位レイヤは整理できました。 しかし、ドメイン層とデータアクセス層の間には依然として問題があります。 現状: Domain Model = Eloquent Model(同一)

    問題: - ドメインモデルがActiveRecordの制約を受ける - テーブル構造の変更がドメインモデルに直接影響 - 複雑な集約(Aggregate)の表現が困難 選択肢: A) 許容する(多くのプロジェクトではこれで十分) B) 分離する(Full Mapping) → プロジェクトの複雑さに応じて判断
  8. 徹底的なCleanArchitecture (Full-Mapping) ここまで行う必要があるか。 プレゼンテーション層 → アプリケーション層 間の詰め替えは賛成ですが、 アプリケーション層 → データアクセス層

    間の詰め替えには懐疑的です。わたしは直接 Domain Modelを渡していいと考えます。その場合下記のように多少簡略化されます。
  9. データの詰め替え戦略 - どこがボトルネックか 入力 出力 これは経験則ですが、一番複雑な詰め替えは 入力の2. です。 また入力の2. が複雑ということは、永続化も同程度であり、結果的に入力の3.

    と出力の3. も複雑になります。 1. プレゼンテーション層 - Request → InputData 2. アプリケーション層 - InputData → Domain Model 3. データアクセス層 - Domain Model → Eloquent Model 1. プレゼンテーション層 - OutputData → ViewModel 2. アプリケーション層 - Domain Model → OutputData 3. データアクセス層 - Eloquent Model → Domain Model
  10. Doctrineの特徴 DoctrineはData Mapper的なORMであり、ActiveRecord系のEloquentとは異なり 下記の特徴を持ちます。 1. 永続化の分離 永続化の対象としてのPHPのClassは、データベース操作のロジックを一切持たない純 粋なPHPオブジェクト(POPO)であり、Eloquentのように特別なClassを継承する必 要もありません。 2.

    Unit of Work ActiveRecord とは異なり、開発者が明示的に永続化のメソッドを呼び出したとき に、まとめてデータベースに書き込みます 3. PHPのクラスに対する直接的なマッピング アノテーションもしくはXMLでクエリ結果とPHPのClassの対照を示すことで、直接必 要なDomanModelを取得可能です