前回の記事
【ミライトデザイン社内勉強会#15】「IDDD本から理解するドメイン駆動設計」輪読会~第6章「値オブジェクト」~ - Qiita
実践DDD本 第7章「ドメインサービス」~複数の物を扱うビジネスルール~ (1/4):CodeZine(コードジン)
オブジェクトが状態を持つ、持たないというのは下記のようなイメージであってる?
- 状態をもつ
- 引数が同じでも、addメソッドを呼び出すたびに値が変わる
class 計算
{
private $num = 0;
public function add(int $n): int
{
return $this->num += $n;
}
}
- 状態を持たない
- 引数が同じ場合、addメソッドを何回呼んでも値は変わらない
class 計算
{
public function add(int $n1, int $n2): int
{
return $n1 + $n2;
}
}
- イメージ的にはこれで問題無いと思う
サービスには状態を持たせないこと。
とあるがドメインサービスが状態を持ってはいけない理由が知りたい
- ドメインサービスはロジックをまとめているからじゃない?
- Entitiyとか値オブジェクトでやっていた振る舞いだけをサービスとして分離したクラスなので、状態を持たせない
- 状態をもつとエンティティや値オブジェクトと役割が被ってしまう
- ドメインサービスはDIとかでユースケースに注入することが多くあるので、他の箇所で使用している可能性がある。それに状態として持たせておくと状態を変更した時の影響がどこに及ぶのかがわからなくなるのではないか。
- ドメインサービスに限らず、 DIで渡してくるものは状態を持たせない方がいい
Entityが内部にプロパティとして他のEntityを持っていることはあるの?
- 上記の図を見ると、EntityがEntityを持っていることはありそう。
- 集約ルートのEntityは他のEntitiyは持てるけど、別集約は持てなさそう。
- Entity = 集約ルート というわけではなさそう
- Entityには他の別集約の識別子だけも持っていて、復元をする。みたいな実装もあると思う
-
戦術的DDD基本原則まとめ - Qiita
集約のメンバはエンティティと値オブジェクトのみ
「リファクタリングによるドメインサービスの導入」で「集約の内部からリポジトリを呼び出すことはあまり望ましくない」とが記されているが、その理由を知りたい。
経験上、集約の内部からリポジトリ(12)を使うことは、できる限り避けるべきだ。 -- ヴァーン・ヴァーノン. 実践ドメイン駆動設計 (Japanese Edition) (Kindle の位置No.6493-6494). Kindle 版.
ドメイン内のサービスは、必要に応じてリポジトリを使える。しかし、集約のインスタンスからリポジトリにアクセスすることは、お勧めできない。
-
経験上🤔
-
集約の中であえてリポジトリを呼ぶメリットがない
- Entitiyの中で外部から値を持ってきたりする処理自体がない
- ドメインがインフラに依存してるからダメ
- ドメインオブジェクトの操作は集約からしかできないようであるべき
リファクタ前のバックログアイテムは値オブジェクトとなっているが、プロダクト集約の処理をドメインサービスに移したことでバックログアイテムが集約になっているがこれはコアドメインの変更がされたと思っていい?
と書いているので、記述ミス?
- この辺りはこの記事だけではわかりにくいと思うので、原本を合わせて読んだ方がわかりやすいかも
- 元々はバックログアイテムは値オブジェクトとしてプロダクトが保有している
-
ビジネス優先度を合計するメソッド
もプロダクトにあった(この時点では何も問題ない)
-
- プロダクトの集約が肥大化してきたのでリファクタを行った
- この時バックログアイテムを集約に変更して、プロダクトから分離した
- リファクタ後のプロダクト、バックログアイテム、どちらとも
ビジネス優先度を合計するメソッド
の置き場所としてはイマイチだったので、サービスクラスを作成した- プロダクトに
ビジネス優先度を合計するメソッド
を設置するとプロダクトEntitiyからリポジトリにアクセスをして、バックログアイテムを取得する必要がある。などの問題があったのでサービスクラスを作成し、そこにビジネス優先度を合計するメソッド
を置いた
- プロダクトに
- 元々はバックログアイテムは値オブジェクトとしてプロダクトが保有している
原本では↑みたいな流れになっている
ドメインサービスとはユースケースから呼ばれるサービスのこと?
ドメインサービスとアプリケーションサービスの違いも知りたい
- ユースケースから呼ばれるサービスをドメインサービスと呼ぶのではなくて、ドメインサービスをユースケースから呼んでいる
- UseCase(Applicationサービス)から呼ばれるサービスというわけではなくて、Entityに持たせられなかった振る舞いを持たせている
- Applicationサービス ≒ UseCase
- Applicationサービスの中のUseCaseっていう認識
- 厳密には違うのかもしれないけど、やってることは同じ
- Applicationサービスで、ドメインサービスやエンティティ、値オブジェクトを利用する
ドメインサービスをどういう時に使うのかあまりイメージわかない、アンチパターンを知りたい
ドメインサービス:エンティティや値オブジェクトの責務ではないドメインモデルのロジック(複数のドメインオブジェクトを使って計算する処理やファサード)
DDDに慣れていない場合、トランザクションスクリプトで処理を書いてしまう可能性があります。
この問題は、使う必要がない箇所でドメインサービスを利用してしまうため、エンティティや値オブジェクトが空っぽの「ドメインモデル貧血症(5章)」を引き起こす危険性があります。
ドメインサービスの「ミニレイヤ」を作ってしまう失敗
検討することなく最初からドメインサービスのレイヤを作ってしまうと、このレイヤが肥大化する傾向にあります。
- 基本はドメインサービスは使わない(使う必要が無いなら使わない)
- ドメインサービスを使う基準は、集約をまたいで処理を書きたくなったら
- ドメインサービスはどのEntitiyにも属さなかった振る舞いを仕方なくドメインサービスに切り出している
次回
【ミライトデザイン社内勉強会#17】「IDDD本から理解するドメイン駆動設計」輪読会~第8章「ドメインイベント」~ - Qiita