はじめに
エリック・エヴァンスのドメイン駆動設計(2004)の第5章 「ソフトウェアで表現されたモデル」を読んで自分的に理解したことを書き出す
結構自分の考えと混じってしまってるので、ここに書いてあることイコール本の内容ではないです
自分の理解の足りないところやおかしいところはバシバシ突っ込んでもらいたいです
他のまとめ
- 第3章 「モデルと実装を結びつける」
- 第4章 「ドメインを隔離する」
- 第5章 「ソフトウェアで表現されたモデル」
- 第6章 「ドメインオブジェクトのライフサイクル」
- [第8章 「ブレイクスルー」] (https://qiita.com/mafuyuk/items/91fb0ecb667fbadeceff)
対象者
- 古いシステムからリプレイスする人
- リリース優先で作った際のリスクを見ておきたい人
- 設計をしていなくて雰囲気で物を作っている人
- エリック・エヴァンスの本を読むと抗えない眠気に襲われる人
ざっくりどんな話?
モデルから実装に落とし込むにあたっての考え方とパターンを説明している章
モデル同士の関連については必須なもののみに限定することでモデルを正確に表現でき、保守性の向上が見込める
モデルを表現する3パターンの要素としてEntities、Value Objects、Servicesがある
Entities、Value Objectsは両方ともオブジェクトとして表すのだが、特性によって区別することで強固な設計を実現する
Servicesに関してはドメインの側面によっては、オブジェクトとしてよりも、アクションや操作として表現したほうが明確になることもあるのでこれらをServicesというパターンに当てはめるようにする
以降では関連、パターン、モジュールについて深掘りしていく
モデルごとの関連
モデル同士の関連について 多対多、一対多、関連なしと関連があればあるほど複雑になる
それを避けるための3つの方法がある
- 関連を辿る方向を強制する
- 限定子を付加して、多重度を効果的に減らす
- 本質的ではない関連を除去する
関連を蒸留し制限することでモデルをより正確なものにし、実装の保守性を向上させることができる
ドメインモデルを表現する3つのパターン
Entities
連続性(continuity)と同一性(identity)によって定義されるオブジェクト
同一性(identity)
- 他のものから対立区分されていることで変わらずに等しくある個の性質
- 形式や履歴に関係なく、各オブジェクトを識別する手段を定義すること
- モデルは同じものであるということが何を意味するのか定義する必要がある
- 上記に関してオブジェクトを属性によって識別できるまたは識別子を付与することで識別できるようにするなどがある
- 属性が同一である別のオブジェクトからも、区別できるものでなければいけない
注意点
プログラミング言語で用意されている同一性の担保機能とドメインのコンテキストで言う同一性は別物として認識しておいたほうが良いということです
たとえばJavaで同一性を表現するには==を利用しますが、これはインスタンスの参照先つまりメモリ上での同一性を見ているので、エンティティの同一性の比較には適していないので同一性の比較が必要な場合は識別子を比較するようなメソッドなどを実装するとよい
(参考になりそうなリンク: エンティティの同一性を表現するためにequalsをオーバーライドすべきか否か)
連続性(continuity)
- 連続性はエンティティのライフサイクルを通じて続き、エンティティが多様な形をとっても変わらない
- 名前が途中で変わっても自分は自分のままみたいなイメージ
同一性にも時系列的な概念はあるっぽいので、EricEvansは強調するためにあえて使ったのかも...
上記を満たすものを、Entitiesとして扱う
Value Objects
- ドメインを表現しているオブジェクトであり同一性を持たないオブジェクト
- あるモデル要素について、その属性しか関心の対象とならないのであれば、その要素を値オブジェクトとして分類すること
- Value Objectsがインスタンス化される際に表現しようとするのはWhatのみであって、WhoやWhichは問わない
- 補足: WhoやWhichは識別子で判別できるのでEntitiesはWhoやWhichを表現できている
- e.g. クレヨンに赤の属性をもたせて2本用意(インスタンス化)したとしてもその2つの差異は気にしない設計
注意点
- 属性としてEntitiesのみを持っている場合でもValue Objectsになる場合もある
- e.g. 経路オブジェクトが属性としてsrc、distを持ち、両方とも都市オブジェクト(Entities)を参照しているパターンなど
- Entityを維持するために必要となる複雑な設計は避けること
- Entityの同一性を追跡することは大事だが、それ以外のオブジェクトに同一性を与えてしまうと、システムの性能を損なったり複雑になったりとするので不要な同一性を外して、Entitiesと区別する
- 不変なものとして扱うこと
- オブジェクトを安全に共有するため
- 渡すオブジェクトを不変にするか、コピーを渡すことで実現する
- 値オブジェクト同士の関連が双方向にある場合は注意が必要
- 単方向の関連または関連なしにするように考える
- またはそもそもValue Objectsとして宣言するということに関して考え直したほうが良いかもしれない
デザインパターンのFlyweight パターンが使えそうなら、パフォーマンス・チューニングのために使うと良い
Services
Services自体は技術的なフレームワークとして一般的なパターンでそれをドメイン層でも利用する
- 独立したインターフェースとして提供される操作
- Entities、Value Objectsのように状態をカプセル化しない
- 名詞よりも動詞にちなんで命名される傾向がある
- 操作名はユビキタス言語に由来される必要がある
- 引数と結果はドメインオブジェクトである必要がある
- サービスは節度を持って使用すべきでEntitiesとValue Objectsからすべてのふるまいを奪ってはいけない
####補足
ここはDDD本で仕入れた内容ではなく 自分の補足です
いつ作るの?
ドメインモデルにまたがるような操作で片方のドメインに振る舞いとして持たせるのが不自然な場合などにドメインサービスを作る場合がある
注意点
ドメインサービスを作る際には本当にサービスとして妥当なパターンとドメインモデルの設計がイケていなかった場合があると思いますので一度ドメインモデルを見直しましょう
ドメインサービスは上記の精査を行い、サービスとして妥当だなと感じたら作成するとよさそうです
優れたサービスの3つの特徴
- 操作がドメインの概念に関係しており、その概念がEntitiesやValue Objectsの自然な一部ではない
- ドメインモデルの他の要素の観点からインタフェースが定義されている
- 操作に状態がない
ドメイン層以外のサービス
ドメイン層以外にもLayered Architectureで定義されているアプリケーション層、インフラストラクチャ層にサービスをパターンとして利用する
以下は例
- アプリケーション層: 資金振替アプリケーションサービス
- 入力を理解する
- 処理を実行するよう、ドメインサービスにメッセージを送信する
- 確認を待つ
- インフラストラクチャサービスを使用して、通知を送信することを決定する
- ドメイン層: 資金振替ドメインサービス
- 必要な講座オブジェクト及び元帳オブジェクトとやり取りし、適切な引き落としと振込を行う
- 結果(振替の可否など)の証跡を出力する
- インフラストラクチャ層: 通知配信サービス
- アプリケーションの指示に従って、電子メールや手紙、その他のメッセージを送信する
Modules(別名 Packages)
モジュールの切り方に関しての考察のヒントが書かれていた
- モジュール間では低結合にする
- 人が一度に考えられる物事の数には限りがある
- モジュール内では高凝集にする
- 首尾一貫していない思考の断片、思考がばらばらに溶け合ったスープのようなもので、理解するのが難しい
- 関連の深いオブジェクトをモジュールでまとめあげる
- モジュールをリファクタリングするのは難しいのだが、設計をきれいにするためにはモジュールのリファクタリングも考慮のうちにいれるとよい
- モジュールとその名前はドメインに対する洞察を反映していなければいけない
- モジュールにはユビキタス言語の一部になる名前をつけること
Tiered Architecture(ティア化アーキテクチャー)
パッケージングの決定に際して強い原動力となるのは、技術的なフレームワークです
有効なフレームワーク標準の例としてはLayered Architectureを強制することですがフレームワークによっては1つのドメインオブジェクトの責務を複数のオブジェクトに広げ、それらのオブジェクトを別個のパッケージに入れることによってティアを作り出すパターンがありTiered Architectureを採用するパターンもありえます
(ティア化とは、責務が断片化された状態のこと)
フレームワークの恩恵を受けるかわりにドメイン設計をきれいに保てなくなる弊害があるはずなのでフレームワーク導入時はよく考えましょう
モデリングパラダイム
オブジェクト指向が主流になってきているが、高度に数学的なドメインやシステム全体に関わる論理的推論に支配されるドメインは、オブジェクト指向パラダイムにうまくなじまない
なのでドメインによってはDDDの手法がマッチしないパターンが有ることを認識しておくこと
効果的にDDDを使用できる範囲で使いましょう
最後に
ドメインモデルをコードに落とし込むパターンについての章でした
Entities、Value Objects、Servicesでドメインモデルを表現し、オブジェクト同士の関連についてとModulesについてざっくりわかったかと思います(ついでにアプリケーション層とインフラストラクチャ層でServicesパターンを使用することも
ドメインモデルを考察し、モデル駆動設計を利用し、ユビキタス言語を使用して、モデルをEntities、Value Objects、Servicesという形でソースコードへに表現する
1章から5章まで学んだことを使うとこんな感じでドメインを表現できそうです
DDD本導入の敷居が低くなれば幸いです