ドメイン駆動設計入門 読書メモ1～3章



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

- 価値ある知識と無価値な知識を慎重に選り分け、コードに落とし込む
- 作り上げられたコードは有用な知識が込められたドキュメントの様相を呈してくる
- 利用者の問題を見極め、解決するための最善手を常に考えていく必要がある
- ドメイン駆動設計は洞察を繰り返しながら設計を行い、ソフトウェア利用者を取り巻く世界と実装を結びつけることを目的としている
- 知識→コード
- ドメインは、「プログラムを適用する対象となる領域」を指す
- 直面している問題を正確に理解することが必要
- ソフトウェアを適用する領域（ドメイン）と向き合い、そこに渦巻く知識に焦点を当てる必要がある。
- ドメイン動設計は、よく観察し、よく表現すること、当たり前の行為を実践するのが難しい。
- 事象や概念を抽象化する作業がモデリングと呼ばれる
- ドメイン駆動設計では、ドメインの概念をモデリングして得られたモデルをドメインモデルと呼びます
- ドメインモデルをソフトウェアで動作するモジュールとして表現したものがドメインオブジェクト
    - **ドメインモデル→（選り分けてソフトウェア化）→ドメインオブジェクト**
- ドメインの概念とドメインオブジェクトはドメインモデルを媒介につながり、お互いに影響しあうイテレーティブ（反復的）な開発が実現される
    - **ドメインの概念⇔ドメインモデル⇔ドメインオブジェクト**
- 知識を表現するパターン
    - 値オブジェクト
    - エンティティ
    - ドメインサービス
- アプリケーションを実現するためのパターン
    - リポジトリ
    - アプリケーションサービス
    - ファクトリ
- 知識を表現する、より発展的なパターン
    - 集約
    - 仕様
- 開発速度を優先したソフトウェアは、柔軟性に乏しく、変化を吸収しきれない
- ドメイン駆動設計はドメインと向き合うことで分析から設計、開発までが相互作用的に影響しあうように努力を重ねることを求める
- プログラムを動かすだけなら簡単だが、動かし続けるのは難しい

# 2.システム固有の値を表現する「値オブジェクト」

- システム固有の値を表現するために定義されたオブジェクトが値オブジェクトと呼ばれるもの
- システムに最適な値が必ずしもプリミティブな値であるとは限らない
    - 氏名はString型で表すことができるが、氏名が逆だったら？
- システムには必要とされる処理に従って、そのシステムならではの値の表現があるはず
- オブジェクトであり値でもある、ゆえに**値オブジェクト**である
- 値オブジェクトの３つの性質
    - **不変である**
    - **交換が可能である**
    - **等価性によって比較される**
- 値オブジェクトにすべきかどうかの判断基準として、「そこにルールが存在しているか」「それ単体で取り扱いたいか」という点を重視する
- ふるまいを持った値オブジェクト
    - 値オブジェクトは不変であるため、計算を行った結果は新たなインスタンスとして返却される
    - オブジェクトに対する操作をふるまいとして一緒にまとめることで、値オブジェクトは自身に関するルールを語るドメインオブジェクトらしさを帯びる
- 値オブジェクトを使うモチベーション
    - 表現力を増す
    - 不正な値を存在させない
    - 誤った代入を防ぐ
    - ロジックの散在を防ぐ
- 値オブジェクトのコンセプトは「システム固有の値を作ろう」という単純なもの
- コードでルールを表現できるのであればそれに越したことはない
- ドメインの概念をオブジェクトとして定義しようとするときに、まずは値オブジェクトに当てはめてみることを検討する

### Kotlinでの値オブジェクトの実装例

```kotlin
// 初期化時にチェックをする
class UserName1 constructor(val value: String) {
    init {
        if (value.length == 0 || value.length > 20) {
            throw IllegalArgumentException("名前は1～20文字にしてください")
        }
    }
}

// ファクトリーメソッドでチェックをする
class UserName2 private constructor(value: String) {
    companion object {
        fun of(value: String): UserName2 {
            if (value.length == 0 || value.length > 20) {
            	throw IllegalArgumentException("名前は1～20文字にしてください")
        	}	
            return UserName2(value)
        }
    }
    val value = value
}

fun main() {
    val user1 = UserName1("")  // エラー
	val user2 = UserName2.of("")  // エラー
}
```

# 3.ライフサイクルのあるオブジェクト「エンティティ」

- エンティティはドメインモデルを実装したドメインオブジェクト
    - 値オブジェクトもドメインモデルを実装したドメインオブジェクト
    - 両者の違いは、同一性によって識別されるか否か
- ユーザーの情報（体重など）が変更されてもユーザーが変更されたわけではない。属性ではなく同一性（identity）により識別される
- エンティティの性質
    - **可変である**
        - 値オブジェクトは不変なため、交換（代入）によって変更を表現しているが、エンティティはふるまいを通じて属性を変更する
    - **同じ属性であっても区別される**
        - 氏名が同一であっても同じ人間ではない。区別するために識別子（identity）が利用される
    - **同一性により区別される**
        - 値オブジェクトの比較処理ではすべての属性が比較対象だが、エンティティの比較処理では識別子（id）だけが比較の対象となる。
- ライフサイクルを持つ。ライフサイクルを持たない場合、ひとまず値オブジェクトとして取り扱うとよい。
- 不変にしておけるものは可能な限り不変なオブジェクトのままにしておく。
- ドメインオブジェクトを定義するメリット
    - **コードのドキュメント性が高まる**
        - 名前は3文字以上20文字以内などをふるまいで定義する
    - **ドメインにおける変更をコードに伝えやすくなる**
        - 名前を3文字以上から6文字以上に変えたい場合、ふるまい一か所の変更で済む
- エンティティを実装しようとしてあいまいさを感じたのであれば、ドメインの捉え方を見つめ直すきっかけとなる

### Kotlinでのエンティティの実装例

```kotlin
class User private constructor(id: Int, name: String) {
    companion object {
        fun create(id: Int, name: String): User {
			checkName(name) 
            return User(id, name)
        }
        fun checkName(name: String) {
     	   if (name.length < 3 || name.length > 20) {
        	    throw IllegalArgumentException("名前は3～20文字にしてください")
           }
        }
    }
    val id = id
    var name = name
    
    fun changeUserName(name: String) {
        checkName(name)
        this.name = name
    }
}

fun main() {
    val user1 = User.create(1, "taro")
    println("${user1.id} ${user1.name}")
    user1.changeUserName("jiro")
    println("${user1.id} ${user1.name}")
}
// 1 taro
// 1 jiro
```

### 参考
ドメイン駆動設計入門 ボトムアップでわかる！ドメイン駆動設計の基本
www.amazon.co.jp/dp/B082WXZVPC