「ドメイン駆動設計入門」を読んで

## この記事は

- エヴァンス本[^1]を**読んでいない**、DDD素人が書いた記事
- 標題の本の内容と周辺知識をまとめて、備忘録とした記事
- あつかう範囲は、本の序盤であるドメインオブジェクト（値オブジェクト、エンティティ）およびドメインサービスまで

[^1]: エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) 大型本 – 2011/4/9

### この記事のつづき

https://qiita.com/t13801206/items/92769ba3a51666bef0bd

## 「ドメイン駆動設計入門」について

- 初版は**2020年3月**に出版された、まだ比較的あたらしい本
  - ※エヴァンス本が翔泳社から出版されたのは**2011年4月**
- 本書はドメイン駆動設計という巨大な試練に立ち向かうための準備である
  - ドメイン駆動設計は、用語が多く、前提知識も必要とされるため難しい
    - 本書では概念の理解には、いったん棚に上げる（概念には触れない）
    - **軽量DDD**やら**ドメインモデル貧血症**やらは、あとで調べる
  - **本書は理解と実践がしやすい実装に関するパターンに集中してボトムアップで解説している**
    - はじめに出てくるのは値オブジェクト
- 例示のコードは`C#`で書かれている
  - [GitHubに実例のサンプルコードが置かれている](https://github.com/nrslib/itddd)
- 定価3,200円

## ドメイン駆動設計とは

> ドメイン駆動設計はまさに知識をコードに埋め込むことを実現するのです（p.002）

具体的に知りたい場合は・・・Qiita検索窓に「title:ドメイン駆動設計」＆LGTM降順ソート

### ドメインに関連する用語の説明

ドメイン|ドメインモデル|ドメインオブジェクト
-|-|-
プログラムを適用する対象となる領域|ドメインの概念を取捨選択して抽象化したもの|ドメインモデルを表現するためのモジュール
会計システムの金銭や帳票、物流システムの倉庫・貨物・輸送手段|道具としてのペン、贈答品としてのペン|ドメインモデルの中から問題解決につながるものを実装したもの

- ドメインの世界の住人と、ソフトウェアの世界の住人が協力してドメインモデルをつくる
  - **ドメインモデルは、現場の人間とソフト開発者の共作である**
- ドメイン・ドメインモデル・ドメインオブジェクトは互いに影響しあい、反復的に開発される
  - これも共作の1つといえる。コードを書くうちに業務ルールが明確化される

### ドメイン駆動のパターン

ここまではドメイン駆動設計の概念の話
ここからは実際にコードとして使えるテクニックの話


- 知識を表現する
  - [値オブジェクト](#値オブジェクト)
  - [エンティティ](#エンティティ)
  - [ドメインサービス](#ドメインサービス)
- アプリケーションを実現する
  - リポジトリ
  - アプリケーションサービス
  - ファクトリー
- 知識を表現する（発展系）
  - 集約
  - 仕様

```plantuml
rectangle アプリケーションを実現する as A {
  usecase "リポジトリ"
  usecase "アプリケーションサービス"
  usecase "ファクトリー"
}

rectangle 知識を表現する as T {
  usecase "値オブジェクト"
  usecase "エンティティ"
  usecase "ドメインサービス"
  usecase "集約"
  usecase "仕様"
  }

A -> T : 利用する
```

## 値オブジェクト

値オブジェクトはドメインオブジェクトの基本である。以下の特徴を持つ

- 不変である
  - 状態が変化することはバグの要因となりうるので、それを除去する
  - デメリット：状態が変化しないので、オブジェクトの一部だけを変えたいときには、あらたにインスタンスを生成せねばならない
- 交換が可能である
  - 値オブジェクトの変更は（値と同じように）、**代入によって交換されることで表現（変更）される**
- 等価性によって比較される
  - 属性の値が同じであれば、異なるインスタンスも同じものであるとみなされる

### どうやって実装するか

先の特徴をクラスとして表現するためには、それなりのコード量が必要となる
標題の本においても、見開き半ページ(p.026)がコードで埋まっている

それらをここに打ち込むのも大変なので、[イメージはこんな感じです](https://ufcpp.net/study/csharp/datatype/record/#data-boilerplate)と有名サイト様のリンクでお茶を濁す

### 値オブジェクトをつかうモチベーション

- 表現力が増す
  - 単なる`string`型ではなく、`string`を複数使ったコンストラクタ`(string productCode,string lotNumber)`のほうが意図が通じやすい
- 不正な値を存在させない
  - コンストラクタやメソッドのガード節で不正な値を検知する
- 誤った代入を防ぐ
  - 値オブジェクトの独自型をつくれば、異なる型はそこに代入できない
- ロジックの散在を防ぐ
  - データとふるまいは近くに置く

「どの粒度までを値オブジェクトとして切り出すか」の判断が難しいときには
「そこにルールが存在しているか」「それ単体で扱いたいか」を自問するといいらしい

### 値オブジェクトをもっと詳しく知りたい人は

クソコードを爆殺する御人の記事をどうぞ！

https://qiita.com/MinoDriven/items/5e69d9bd028aa350e2c4

### 値ほか、値オブジェクトに関する関連記事

- [recordをDDDの値オブジェクトとして利用する際の注意](https://qiita.com/miswil/items/b21aa75a3c69e5c02a02)
  - C# 9.0 から導入された`record`を使えばよいものかと思っていたけど、そうではないということに気づかされました
  - `with`をつかうと、**属性値を変更した代入が可能**となってしまい、**コンストラクタの検証をすり抜ける**のがダメそう
    - 詳細は元記事をご覧ください
- [システム固有の値を表現する「値オブジェクト」のまとめと感想](https://qiita.com/flyaway/items/305df0382d9a284820b6)
  - 同じ本を読んだ方が値オブジェクトだけにフォーカスしてまとめていらっしゃいます

### 現時点での値オブジクトに対する勝手なイメージ

値オブジェクトは石ころのイメージ

- 1つ1つを区別する必要がない
- 別の形の石ころが必要になったら、その形の石ころを探してつかう（＝交換可能）
- 大きさや形（＝属性）が同じ石ころは、同じものとして扱っても支障がない

#### 一方で、石材店の親父にとっての石ころは・・・

石材店の親父にとっては、**1つ1つの石ころには意味があり、判別可能**であるかもしれない
同一の対象物（ここでの石ころ）であっても、それが属するドメインによって1つ1つが意味をもつ

そういった類のものは、次節の[エンティティ](#エンティティ)として扱うこともある

石材店の親父（＝ドメインエキスパート）と共にドメインモデルを練り上げることで、その概念を表すにふさわしいドメインオブジェクトの形態が決まる（と理解している）  

## エンティティ

エンティティは値オブジェクトと対をなすドメインオブジェクトである
簡単にいえばSNSのアカウントのように、識別子があり、生成され、いずれ削除されうるもの

- 可変である
  - 状態が変えられる。オブジェクトを観測するたびに違うふるまいをする可能性がある
- 属性が同じでも区別される
  - 同姓同名の人間が2人以上いるとき、その人たちをすべて同一人間として扱ったりしない
- 同一性をもつ
  - 識別子により同一性・連続性が担保される

## ドメインオブジェクトをつかうメリット

[値オブジェクト](#値オブジェクト)と[エンティティ](#エンティティ)という、2つのドメインオブジェクトが登場した
これらのドメインオブジェクトをつかうメリットは2つある

### メリット1：コード自体がドキュメントになる

値オブジェクトの「表現力が増す」と言いたいことは同じで
単なるプリミティブな型、たとえば`string`をつかうのではなく
独自に型をつくることで、コードにルールが埋め込まれて、それがドキュメントとなる

### メリット2：ドメインルールの変更をコードに反映しやすくなる

もう1つはオブジェクト指向の基本であるところの**カプセル化**である
データとふるまい（メソッド）を1か所にまとめることで、**変更箇所を極小化**できる

逆にオブジェクトがデータを保持するだけで、ふるまいが外に漏れ出ることを、**ドメインモデル貧血症**というらしい

コードの不自然さを解消する反面、この貧血症を起こす危険性をはらむのが、次節の[ドメインサービス](#ドメインサービス)である

## ドメインサービス

> ドメインサービスは不自然さを解消します (p.065)

値オブジェクトやエンティティに持たせると不自然になるふるまいはサービスが担う

たとえばユーザーアカウント（＝エンティティ＝区別されるもの）を新規作成するためには、**あたらしく作るエンティティの識別子が既存のものと衝突しないこと**を確認する必要がある
その確認作業をエンティティ自身にもたせるのは不自然である

```cs
class User
{
  public bool Exists(User user)
  {
    // ユーザーがユーザーを引数にして、存在するか判断する。これは不自然
  }
}
```

その場合は、サービスクラス`class UserService`にメソッド`Exists(User user)`を持たせればよい

ドメインサービスは値オブジェクトやエンティティが担えないふるまいを代行する事務係であるため、**ドメインサービス自身は状態をもたない**
（ある種ユーティリティクラスのようなものだと捉えている）

### ドメインサービスの乱用はドメインモデル貧血症をおこす

ふるまいをドメインサービスに任せっきりになると、ドメインオブジェクトは`getter/setter`だけのデータの入れ物になり下がる
ドメインサービスを神ユーティリティクラスに進化させてしまわないように、まずは値オブジェクトやエンティティにふるまいを実装することを考える
不自然さを解消する目的においてのみ、ドメインサービスへふるまいを移行する

## おわりに

標題の本の1/4くらいまでについて触れた

実際のコード事例が少ない文字だけの記事になってしまったが、巷には実践例があふれているのでよしとする

標題の本の付録には、「Visual Stduioにおいてソリューション構成をどうすべきか」が書かれている
何の名前でどうフォルダ分けするが、まず初めに悩むところなので、この辺はまた後日読みたい

## おまけ

ちょうどコレを書いているときに、[zennに類似の投稿があった](https://zenn.dev/ryoatsuta/articles/69611bda86149f)ので、リンクさせていただく