[読書メモ]ドメイン駆動設計 モデリング/実装ガイドをざっくり読んでみた
最初に
ドメイン駆動設計 モデリング/実装ガイドを読んでいる際に、個人的につけていたメモです。
いわゆるまとめ記事ではなく、内容も飛び飛びで自分の感想も多々混ざっていますが、ご了承ください。
第1章 DDD概要
何かしらの問題を解決したい領域のことをドメインと呼び、ドメインの知識を理解して、問題を解決するために特定の側面を切り出して抽象化したもがモデルになる。
そしてモデルをソフトウェアに落とし込むことでドメインの問題を解決できる、という順序になる。
つまり、ドメインを正確にとらえて解決すべき問題や組織内で用いられているユビキタス言語を整理して共通認識をそろえることが重要となる。
モデリングを繰り返し、ソフトウェアに落とす営みのサイクルを回し続けることで、問題解決力が高いソフトウェアを生み出すことにつながる。
第2章 モデリングから実装まで
モデリングから実装までのステップの一例として、ユースケース図に起こしてからドメインモデリングを行い、アーキテクチャを決めてコードに落としていくというプロセスがある。
ドメインモデルの図示化の中で集約内外の言葉の意味が良くわからなかったのでAIと問答をしたところ、勉強になったので以下にメモをしておく。
集約とは何か?
集約とは、一貫性を保つべきオブジェクトのまとまりのことを指す。
集約外のオブジェクトへの参照行う際に、はオブジェクトそのものにアクセスするのではなくて、参照したいオブジェクトのIDを使って参照する。
集約内のオブジェクトへの参照はインスタンス参照として直接アクセスする。
なぜ集約内外で参照方法を分けるのか?
集約外をID参照にするのは、「責任範囲と整合性の境界」を守り、トランザクションを分離し、性能と保守性を高めるため。
つまり、本来別々で整合性を担保すべき情報が互いに自由に参照・更新できる状態にあると、データとしての不整合が生まれやすいもろい構造になるので、トランザクションの境界(一貫性を保証すべき最小単位の範囲)を明確にすることが重要ってこと見たい(多分)
などこねこねしていたら3章に書いてあった。。。
第3章 DDD固有のモデリング手法
集約ルート
集約のオブジェクト(=強い整合性を一括で保証すべきオブジェクトの 集合)を扱うときの親となるオブジェクトを集約ごとに決めたものが集約ルートとなる。
集約外のオブジェクトによって、子となるオブジェクトへのアクセスが必要な場合は必ず集約ルートを通じてでしかアクセスできないように縛る(外部は子エンティティを直接保持せず、IDで参照する)ことで、整合性の確保が可能となり、堅牢な構造となる。
境界づけられたコンテキスト
1つの巨大モデルですべてのドメインに共通する部分を表現しようとすると、かえって複雑になってしまい、変更に強い構造からは外れてしまう。
そこで、用語とビジネスルールの一貫性が保てる最大のスコープを明示的に区切り、各スコープの内部で “独立したモデル” を定義する。
この、一貫したモデル群が適用される明示的に示されている範囲のことを「境界付けられたコンテキスト」と呼ぶ。
商品で言えば、販売という範囲(コンテキスト)と、配送という範囲(コンテキスト)のなかでは用いるモデルは分かれていることが望ましいため領域として分割されて、それぞれ別のコンテキストの中で独立したモデルが用いられる。
第4章 設計の基本原則
凝集度
モジュールがどのような傾向でもってまとめられているかを表す指標である凝集度。
参考:https://note.com/cyberz_cto/n/n26f535d6c575
凝集度が高いというのはすなわち、1つのモジュール(クラス/関数/パッケージなど)の中に、
同じ目的・同じ変更理由で動く要素だけが集まっている状態を意味しており、いわゆる「機能的凝集」となっている状態を指す。
値オブジェクトをきちんと実装して、値オブジェクトを用いた判断・加工・計算のロジックを値オブジェクトの内部に閉じ込めてインターフェースだけを外部に公開するように寄せていく実装は機能的凝集に近づける行為ともいえるのかもしれない。
第5章 アーキテクチャ
オニオンアーキテクチャ
訳が分からない。なんたらアーキテクチャ系、完全に分かったとやっぱりわからんの繰り返しになる。
色々見た感じ、とりあえずドメイン層に対してのみ唯一の依存を持つようにするべきであって、アプリケーション層からインフラ層への依存はインターフェースへの依存になるように、ドメイン層にインフラ層のインターフェースを実装するっていうのがキモらしい。
アプリケーション層は具体的にどのような方法を用いて実装されているかは知らないけれど、なにがしかの方法で永続化してねという指示を実行する構造にするってことらしい。
結局、○○アーキテクチャ系で目指したいところは本質的にはドメイン層に依存させようっていう思想だけは共通認識としてあるのだろう。多分。良くわからん。
第6章 ドメイン層の実装
ドメインモデルを表現するもの
ドメインモデルを表現するものとしてエンティティと値オブジェクトがある。
これら2つの大きな違いとして同一判定の方法がある。エンティティは識別子、値オブジェクトは保有する値を比較して、ある2つのエンティティ、値オブジェクトが同一かどうかの判定を行う。
とあり、わからんでもないがそうなのかという感じだったので改めて整理してみた。
エンティティ | 値オブジェクト | |
---|---|---|
同一性 | ID ベース | 値の完全一致 |
状態遷移 | あり(ライフサイクル) | 基本なし(イミュータブル) |
不変条件の責任 | 自分で保持/更新 | 自分の中で常に成り立つ |
具体例 | 注文 Order 、顧客 Customer 、銀行口座 BankAccount
|
金額 Money 、メールアドレス Email 、座標 GeoPoint
|
やはり同一性がキモとなる。IDベースで同一性を担保するのであれば、それ以外の値は変化しても良いということになるため、状態遷移の有無で違いが生まれる。
また、変化する際に不変条件(絶対守られるべきルール)をエンティティ自身が担保しないといけないため、不変条件の責任がエンティティ自身にある一方で、値オブジェクトは変化を前提としないため、生成された時点で必ず成り立つことが保証されている。
ということは、ドメインモデルを表現する際のエンティティの使い分けはそれが何をベースとして同一性を判断しているかを考えながら行うことが求められる。
第7章 ユースケース(アプリケーション層)の実装
ユースケースからの戻り値クラス
ユースケース層からプレゼンテーション層に何かしらの値を返すときには、ドメインオブジェクトをそのまま渡すのではなくて、専用のクラスに詰め替えて渡してあげるほうが長期的にみると望ましい。
そうしないと、ドメインオブジェクトにプレゼンテーション層で表示に用いるための文字列生成メソッド的なのをはやして用いることになってしまいがちで、プレゼンテーション層の知識がドメインオブジェクトににじみ出ることになるからだそう。
これは最近自分のコードをいじっていて痛感した。ドメインオブジェクトに関連したロジックを委譲すれば呼び出し側がシンプルになるというのは間違いではないのだけれど、使いどころを間違えるとファットなドメインオブジェクトが出来上がるだけだもんなぁと思った。ユースケース層に実装している処理がドメインオブジェクト側で巻き取れないかを考えるのは大事だけれど、プレゼンテーション層はまた別ということを忘れないようにしたい。
第8章 CQRS
CQRS(Command Query Responsibility Segregation)とは参照(Query)に使うモデル と 更新(Command)に使うモデル を分離するアーキテクチャパターンのことを指す。
DDDにおいて、リポジトリを介した永続化の値の入れ物としてドメインオブジェクトが用いられることになる。そのため、実装されるモデルが更新には便利である一方で参照する際に望ましい形式とは限らない。
ユースケースとして、複数のモデルを組み合わせて何らかの画面表示用のオブジェクトを組み立てたいときなどに値の詰め替えが都度発生し、パフォーマンスが低下しがちになる。可読性も下がりがちとなる。
そこで、参照用のモデルと更新用のモデルをきちんと分けて、用途に応じて使い分けるのがCQRSの概要となる。
特定のユースケースに特化したモデルを定義し、リポジトリは参照用のモデルに詰め替えてユースケースに返してあげることで後続の処理がきれいになる。
ただし、複数のユースケースに流用するために大きなモデルを作って無理くり使い分ける無理な共通化をすると、単一責任の原則に違反することとなり、コードにゆがみが生まれるので良くない。
常に何のためのモデルなのかがスパッと言語化できる状態を保ちながらモデルをメンテナンスする気概を持つことが重要となるのかもしれない。
第9章 プレゼンテーション層の実装
プレゼンテーション層において、値のオブジェクトを生成してユースケース層に渡すのはあまりよろしくないらしい。
なぜなら値オブジェクトを組み合わせて何らかの要望に応えるサービスを提供するのがユースケースの責務となるからみたい。
とはいえ、ユーザーからの入力にごみが入っていないかを最低限チェックしてから後続に渡すのもプレゼンテーション層の責務だとも思うので、バリデーションの境界がちょっと判断つかないなとも思った。
依存関係としてドメイン層に影響を与えているわけでもなく、プレゼンテーション層がドメイン層に依存する矢印は適切なものではあるので一長一短なのか??よくわからん!
まぁドメインオブジェクトをフル活用して何らかの処理を成功させたり、失敗してたらそれを通知したり、サービス全体のオーケストレーションはユースケース層が担うべき、という話はその通りなので納得した。
第10章 アーキテクチャ全般・ライブラリなど
バリデーションについてプレゼンテーション層とドメイン層で重複してチェックしたくなることあるよね問題について書かれていた。これは9章についてちょっと書いた内容と被っていた。
結論いうと、プレゼンテーション層で二重にバリデーションをかけることによるメリット・デメリット両方あるので適宜使い分けてねということだった。
デメリットの1つとしてバリデーションロジックの変更があった時の変更漏れがあげられていたが、最悪ドメイン層で防御はできるので、ドメイン層でのバリデーションは最優先で実装すべきだなと思った。
Discussion