この記事は
- エヴァンス本1を読んでいない、DDD素人が書いた記事
- 標題の本の内容と周辺知識をまとめて、備忘録とした記事
- あつかう範囲は、本の序盤であるドメインオブジェクト(値オブジェクト、エンティティ)およびドメインサービスまで
この記事のつづき
「ドメイン駆動設計入門」について
- 初版は2020年3月に出版された、まだ比較的あたらしい本
- ※エヴァンス本が翔泳社から出版されたのは2011年4月
- 本書はドメイン駆動設計という巨大な試練に立ち向かうための準備である
- ドメイン駆動設計は、用語が多く、前提知識も必要とされるため難しい
- 本書では概念の理解には、いったん棚に上げる(概念には触れない)
- 軽量DDDやらドメインモデル貧血症やらは、あとで調べる
-
本書は理解と実践がしやすい実装に関するパターンに集中してボトムアップで解説している
- はじめに出てくるのは値オブジェクト
- ドメイン駆動設計は、用語が多く、前提知識も必要とされるため難しい
- 例示のコードは
C#
で書かれている - 定価3,200円
ドメイン駆動設計とは
ドメイン駆動設計はまさに知識をコードに埋め込むことを実現するのです(p.002)
具体的に知りたい場合は・・・Qiita検索窓に「title:ドメイン駆動設計」&LGTM降順ソート
ドメインに関連する用語の説明
ドメイン | ドメインモデル | ドメインオブジェクト |
---|---|---|
プログラムを適用する対象となる領域 | ドメインの概念を取捨選択して抽象化したもの | ドメインモデルを表現するためのモジュール |
会計システムの金銭や帳票、物流システムの倉庫・貨物・輸送手段 | 道具としてのペン、贈答品としてのペン | ドメインモデルの中から問題解決につながるものを実装したもの |
- ドメインの世界の住人と、ソフトウェアの世界の住人が協力してドメインモデルをつくる
- ドメインモデルは、現場の人間とソフト開発者の共作である
- ドメイン・ドメインモデル・ドメインオブジェクトは互いに影響しあい、反復的に開発される
- これも共作の1つといえる。コードを書くうちに業務ルールが明確化される
ドメイン駆動のパターン
ここまではドメイン駆動設計の概念の話
ここからは実際にコードとして使えるテクニックの話
値オブジェクト
値オブジェクトはドメインオブジェクトの基本である。以下の特徴を持つ
- 不変である
- 状態が変化することはバグの要因となりうるので、それを除去する
- デメリット:状態が変化しないので、オブジェクトの一部だけを変えたいときには、あらたにインスタンスを生成せねばならない
- 交換が可能である
- 値オブジェクトの変更は(値と同じように)、代入によって交換されることで表現(変更)される
- 等価性によって比較される
- 属性の値が同じであれば、異なるインスタンスも同じものであるとみなされる
どうやって実装するか
先の特徴をクラスとして表現するためには、それなりのコード量が必要となる
標題の本においても、見開き半ページ(p.026)がコードで埋まっている
それらをここに打ち込むのも大変なので、イメージはこんな感じですと有名サイト様のリンクでお茶を濁す
値オブジェクトをつかうモチベーション
- 表現力が増す
- 単なる
string
型ではなく、string
を複数使ったコンストラクタ(string productCode,string lotNumber)
のほうが意図が通じやすい
- 単なる
- 不正な値を存在させない
- コンストラクタやメソッドのガード節で不正な値を検知する
- 誤った代入を防ぐ
- 値オブジェクトの独自型をつくれば、異なる型はそこに代入できない
- ロジックの散在を防ぐ
- データとふるまいは近くに置く
「どの粒度までを値オブジェクトとして切り出すか」の判断が難しいときには
「そこにルールが存在しているか」「それ単体で扱いたいか」を自問するといいらしい
値オブジェクトをもっと詳しく知りたい人は
クソコードを爆殺する御人の記事をどうぞ!
値ほか、値オブジェクトに関する関連記事
-
recordをDDDの値オブジェクトとして利用する際の注意
- C# 9.0 から導入された
record
を使えばよいものかと思っていたけど、そうではないということに気づかされました -
with
をつかうと、属性値を変更した代入が可能となってしまい、コンストラクタの検証をすり抜けるのがダメそう- 詳細は元記事をご覧ください
- C# 9.0 から導入された
-
システム固有の値を表現する「値オブジェクト」のまとめと感想
- 同じ本を読んだ方が値オブジェクトだけにフォーカスしてまとめていらっしゃいます
現時点での値オブジクトに対する勝手なイメージ
値オブジェクトは石ころのイメージ
- 1つ1つを区別する必要がない
- 別の形の石ころが必要になったら、その形の石ころを探してつかう(=交換可能)
- 大きさや形(=属性)が同じ石ころは、同じものとして扱っても支障がない
一方で、石材店の親父にとっての石ころは・・・
石材店の親父にとっては、1つ1つの石ころには意味があり、判別可能であるかもしれない
同一の対象物(ここでの石ころ)であっても、それが属するドメインによって1つ1つが意味をもつ
そういった類のものは、次節のエンティティとして扱うこともある
石材店の親父(=ドメインエキスパート)と共にドメインモデルを練り上げることで、その概念を表すにふさわしいドメインオブジェクトの形態が決まる(と理解している)
エンティティ
エンティティは値オブジェクトと対をなすドメインオブジェクトである
簡単にいえばSNSのアカウントのように、識別子があり、生成され、いずれ削除されうるもの
- 可変である
- 状態が変えられる。オブジェクトを観測するたびに違うふるまいをする可能性がある
- 属性が同じでも区別される
- 同姓同名の人間が2人以上いるとき、その人たちをすべて同一人間として扱ったりしない
- 同一性をもつ
- 識別子により同一性・連続性が担保される
ドメインオブジェクトをつかうメリット
値オブジェクトとエンティティという、2つのドメインオブジェクトが登場した
これらのドメインオブジェクトをつかうメリットは2つある
メリット1:コード自体がドキュメントになる
値オブジェクトの「表現力が増す」と言いたいことは同じで
単なるプリミティブな型、たとえばstring
をつかうのではなく
独自に型をつくることで、コードにルールが埋め込まれて、それがドキュメントとなる
メリット2:ドメインルールの変更をコードに反映しやすくなる
もう1つはオブジェクト指向の基本であるところのカプセル化である
データとふるまい(メソッド)を1か所にまとめることで、変更箇所を極小化できる
逆にオブジェクトがデータを保持するだけで、ふるまいが外に漏れ出ることを、ドメインモデル貧血症というらしい
コードの不自然さを解消する反面、この貧血症を起こす危険性をはらむのが、次節のドメインサービスである
ドメインサービス
ドメインサービスは不自然さを解消します (p.065)
値オブジェクトやエンティティに持たせると不自然になるふるまいはサービスが担う
たとえばユーザーアカウント(=エンティティ=区別されるもの)を新規作成するためには、あたらしく作るエンティティの識別子が既存のものと衝突しないことを確認する必要がある
その確認作業をエンティティ自身にもたせるのは不自然である
class User
{
public bool Exists(User user)
{
// ユーザーがユーザーを引数にして、存在するか判断する。これは不自然
}
}
その場合は、サービスクラスclass UserService
にメソッドExists(User user)
を持たせればよい
ドメインサービスは値オブジェクトやエンティティが担えないふるまいを代行する事務係であるため、ドメインサービス自身は状態をもたない
(ある種ユーティリティクラスのようなものだと捉えている)
ドメインサービスの乱用はドメインモデル貧血症をおこす
ふるまいをドメインサービスに任せっきりになると、ドメインオブジェクトはgetter/setter
だけのデータの入れ物になり下がる
ドメインサービスを神ユーティリティクラスに進化させてしまわないように、まずは値オブジェクトやエンティティにふるまいを実装することを考える
不自然さを解消する目的においてのみ、ドメインサービスへふるまいを移行する
おわりに
標題の本の1/4くらいまでについて触れた
実際のコード事例が少ない文字だけの記事になってしまったが、巷には実践例があふれているのでよしとする
標題の本の付録には、「Visual Stduioにおいてソリューション構成をどうすべきか」が書かれている
何の名前でどうフォルダ分けするが、まず初めに悩むところなので、この辺はまた後日読みたい
おまけ
ちょうどコレを書いているときに、zennに類似の投稿があったので、リンクさせていただく
-
エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) 大型本 – 2011/4/9 ↩