所感
ドメイン駆動設計に関して本格的に学ぼうと思って読んだ1冊
ドメインモデルをうまく表現することの利点や技法などをバラバラだった知識をまとめられた印象
ビジネスドメインを丁寧にモデリングして扱えるようにしたい
Chapter2
「システムに最適な値が必ずしもプリミティブな値であるとは限らない」
ドメイン固有の値をどう扱うと良いのかにフォーカスすることが大事。
イミュータブルであることによりデータの信頼性が向上されるし、それにより交換可能になる。(逆に言うと交換以外で値を変更できない)
クラスとして実装していくことで値オブジェクト固有のバリデーションロジックも内包できるというのも扱いやすくなるポイントかなとも思う。
値オブジェクトとして扱う基準
また、値オブジェクトとして扱うべきかどうかの判断基準として「そこにルールが存在しているか」「それ単体で取り扱いたいか」という点を筆者が重視しているという点も印象に残った。
ドメイン内で特別視されている(ドメイン固有の呼び名が付いているなど)の場合は、値オブジェクトになり得るのかなと思った。
値オブジェクトに関しては下記のpodcastもとても参考になる。
https://anchor.fm/textafm/episodes/2--The-Power-of-Constraints-ejufu0
Chapter3
値の同一性
- エンティティは識別子(ID)で同一性を判断
- 値オブジェクトは値で同一性を判断
- 別のオブジェクトだとしても値が同じであれば同一とみなす
エンティティの判断基準
「ライフサイクルが存在し、そこに連続性が存在する」ことがエンティティかどうかの基準。
作成されて誕生し、削除されて死を迎える。つまりCRUD処理が発生し得る可変なオブジェクト。
値オブジェクトは不変であるため、エンティティではないということが分かる。
ドメインオブジェクトを定義するメリット
印象に残ったのは「仕様書というのはマクロな要件については有効であってもミクロな要件については無力であることが多い」ということ。
その通りであるし、そこまで記述する必要がないからそうなっているはず。
だからこそ、コードからその要件を汲み取りやすくしておく必要があるなと再認識した。
クラスにドメインオブジェクトのルールを記述して凝集度は高く保つというのは、ドキュメンテーションという文脈においても重要。
Chapter4
サービスへのロジック切り出しはドメインモデル貧血症を引き起こす
ドメインモデルの振る舞いをサービスに切り出しすぎると、ドメインモデルがデータだけを保持している無口なオブジェクトになってしまう。それによってドメインモデル自身が語るべきことを語っていない状態が出来上がってしまう。
データと振る舞いが点在してしまうことで可読性や保守性に欠けてしまう。
そうならないために、ドメインモデルに不自然な振る舞いのみサービスに切り出すことが重要。
複数のモデルを横断する操作を行う際など、ドメインモデル固有ではないロジックが出てきた場合は検討の余地あり。
Chapter5
ビジネスロジックをより純粋なものに昇華する
リポジトリパターンを利用して永続化処理を抽象化して隠蔽することで、ビジネスロジックを簡潔にできる。
自分の会社のフロントエンドアプリケーションはリポジトリパターンを利用しており既知の知識としてある。
だからこそ今一度下記のようなアンチパターンを実装してしまわないように意識したい。
https://qiita.com/mikesorae/items/ff8192fb9cf106262dbf
Chapter6
アプリケーションサービスはオブジェクトの操作に徹する
ドメインロジックの責務外の振る舞いを記述することに徹する。
サービスオブジェクトはロジックが雑多になりがち(ドメインロジックが記述されてる / 責務が複数等)なので、今一度サービスクラスを作る際は注意したいと思う。
https://qiita.com/joker1007/items/25de535cd8bb2857a685
Chapter7
依存を避けるよりコントロールせよ
抽象に依存することが重要。依存関係逆転の原則。
抽象に依存することで変更箇所が最小になる。改めて思うのは良い設計は変更容易性が担保されていること。
Chapter12
オブジェクトの境界線が秩序をもたらす
デメテルの法則(最小知識の原則)を元にコード設計する。
Wikipediaによると、デメテルの法則の基本骨子は下記である。
基本的な考え方は、任意のオブジェクトが自分以外(サブコンポーネント含む)の構造やプロパティに対して持っている仮定を最小限にすべき
https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%A1%E3%83%86%E3%83%AB%E3%81%AE%E6%B3%95%E5%89%87
つまり、他のオブジェクトの情報(どういう状態/値であるのか等)を知らなくて良いということ。
「尋ねるな、命じろ」というソフトウェアの格言があるが、その通りに設計していけばルールが漏れ出すのを防げる。
変更が面倒な時は大体同じロジックが書かれているので、改めて意識してコードを書いていきたい。
下記資料の図が分かりやすい。
https://qiita.com/TakeshiFukushima/items/0b16aef3d320a2ccb7c3#%E8%A8%AD%E8%A8%88%E5%8E%9F%E5%89%87%E3%81%8C%E5%AE%88%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E4%BE%8B
Chapter13
仕様オブジェクトがドメインルールの点在を防ぐ
仕様オブジェクトについて
ドメインの重要なルールだが処理が複雑になってしまうロジックを格納するオブジェクト
目的としては、ロジックの点在を防ぐこと。
例えば、こういったロジックはサービス層で実装されがちだが、それだとドメインルールが点在してしまう。
そういった状況を防ぐために仕様オブジェクトでロジックをラップする。
リポジトリのフィルターとしても機能する
リポジトリに仕様オブジェクトを渡してメソッドを呼び出すようにすることで、ドメイン知識を記述せずに済むようになる。
// 仕様オブジェクト
public class CircleRecommendSpecification {
private readonly DateTime executeDateTime;
public CircleRecommendSpecification(DateTime executeDateTime) {
this.executeDateTime = executeDateTime;
};
public bool IsSatisfiedBy(Circle circle) {
if(circle.CountMembers() < 10) {
return false;
}
return circle.Created > executeDateTime.AddMonths(-1);
};
}
// リポジトリ
public class CircleRepository: CircleRepository {
private readonly ICircleRepository circleRepository;
private readonly DateTime now;
...
public CircleGetRecommendResult GetRecommend(CircleGetRecommendRequest request) {
var circleRecommendSpecification = new CircleRecommendSpecification(now);
// リポジトリに仕様を引き渡して抽出(フィルタリング)
var recommendCircles = circleRepository.Find(circleRecommendSpecification)
.Take(10)
.ToList();
return new CircleGetRecommendResult(recommendCircles);
};
}
Chapter14
レイヤーの隔離と責務の分割
本書ではドメイン駆動設計がアーキテクチャに求めることは、ドメインオブジェクトが渦巻くレイヤーを隔離して、ソフトウェア特有の事情からドメインオブジェクトを防衛することと説明されている。
レイヤードアーキテクチャやクリーンアーキテクチャなどのアーキテクチャも依存の方向を定めて、レイヤーごとに責務を分割している。ドメイン駆動設計の文脈上で最も重要なのはドメインの隔離を促すこと。
Chapter15
軽量DDDに陥らないこと
デザインパターンなどを盲目的に利用してそれで終わらないこと。
重要なのはドメインの本質に向き合い続けること。ユーザーを知り対話を続けドメインを改善していくことを意識したい。
まとめ
依存の方向性 / 責務の分割 / ロジック隠蔽を意識したいと思った。
パターンはある程度分かったが、エリックエヴァンスの書籍を読んで根本的な思想の部分を知りたい。