【技術書まとめ】『ドメイン駆動設計 モデリング/実装ガイド』を読んだまとめ

# 第 1 章 DDD 概要

- DDDとは？
    - ソフトウェア開発手法の一つ
        - ある領域の問題を解決する
            - モデリングによってレバレッジをかける
- 良いモデルとは？
    - 問題解決のために特定の側面を抽象化したもの
        - 「特定の」とは？
            - 履歴書
                - 名前や経歴や志望理由は必要
                - 筆圧や履歴書の製造元は必要ない
                    - どの要素を取り込むか？
                        - 抽象化
                            - モデリング
        - 問題を解決できなければ意味がない
            - 良くないモデル
        - ドメインエキスパートと会話する
            - モデルは永遠に未完成
                - 常に改善サイクルを回す
                    - エンティティやリポジトリ
- DDDループ
    - ドメイン
        - モデル
            - ソフトウェア
- モデルの改善ツール
    - ユビキタス言語
    - 境界づけられたコンテキスト
- 重要な考え方
    - 課題ドリブン
        - 課題を明確にする
            - 「DDDをやるからユビキタス言語の用語集をとりあえず作ろう」
                - 結局メンテナンスされないで放置される
    - 小さく初めて、小さく失敗する
        - 改善ループ
- Q&A
    - どんなものにDDDが向いているか
        - 課題解決が複雑なもの
            - 簡単なCRUDだけならオーパースペックになる


## 第 2 章 モデリングから実装まで

- ドメインモデリングのアウトプットは？
    - ユースケース図
        - 棒人間が何をするかを書く
            - 解決すべき課題を明らかにする
        - スコープを決める
    - ドメインモデル図
        - 代表的なプロパティのみ
            - メソッドは書かない
        - ルール/制約（ドメイン知識）を吹き出しに書く
            - 図にしてもいい
        - 関連性を書く
        - 多重度を書く
        - 集約の範囲を書く
        - 具体例を書く
- ドメインモデルの知識を表現した実装例
    - タスクは未完了状態で作成される・タスクは3回だけ1日ずつ延期できる
        - ドメイン知識はドメイン層に書く
            - 外部から`createTask`や`task.postpone()`を呼び出すだけ
                - 何をしたいか（What)だけを示す
- ドメイン層オブジェクト設計の基本方針
    - ドメインモデルの知識を対応するオブジェクトに書く
        - ふきだしの内容
    - 常に正しいインスタンスしか存在させない
        - 確実にデータの整合性が保証される
            - どうやるか？
                - 生成条件の強制
                    - 生成メソッドでしか作れないようにする
                        - デフォルトコンストラクタも無くす
                            - 全ての値が未設定だから
                - ミューテーション条件の強制
                    - ミューテーションメソッドのみ外部に公開する
                        - セッターがない
    - テストコードを書く
- Q&A
    - ユビキタス言語はどうやって管理する？
        - ドメインモデル図自体がユビキタス言語を定義する
            - 自然とメンテナンスしていきやすい
        - アプリ画面で使われる言葉に反映する
    - なぜユースケース図を使うのか？
        - 「アプリとしてどう使うのか」を定義するのが一番開発につながりやすかったから


## 第 3 章 DDD 固有のモデリング手法

- 集約
    - ルール
        1. 強い整合性が必要なものは1つの集約にする
            - 部と部員
                - ４人以下になると未承認になる
                    - 部集約
                        - 集約ルートは部
            - 生徒は生徒集約にする
                - オブジェクトはどれかの集約に必ず所属する
        2. トランザクションを1つにする
            - 集約単位でリポジトリから取得する
                - 渡すときも同じ
                    - 1トランザクションで集約内の全てのオブジェクトを更新する
                        - 整合性が崩れない
    - 集約の境界の決め方
        - どれだけ整合性が必要か？
        - トランザクション範囲は適切か？
            - 部集約の上に学校集約を作ると更新ごとに全ての部をロックする
                - 不必要なロックをうむ
- 境界づけられたコンテキスト
    - モデルの共有
        - 販売部の人から見た商品
            - 商品名や価格や在庫
        - 配送部
            - 商品名や配送先や配送状況
        - さらに増えていく……
            - 統一したモデルは難しい
                - 適用される範囲を区切る
                    - 境界づけられたコンテキスト
                        - 販売コンテキスト
                        - 配送コンテキスト
- Q&A
    - ドメインモデル図を考えるときにDBを意識してしまう
        - NoSQLのデータストアと考える
            - 純粋なオブジェクト
        - 決め切らないで実装してからモデルに戻るのもいい


## 第 4 章 設計の基本原則

- 凝集度・結合度
    - 「高凝集・低結合」
        1. 理解しやすい
        2. 拡張しやすい
        3. 修正時にバグが生まれにくい
        4. 再利用しやすい
        5. テストしやすい
- 凝集度
    - 「このクラスは何をするクラスか？」
        - 責務
    - 低凝集な実装
        - 答えられない
    - 高凝集な実装
        - クラスが何をするかわかる
            - Counter
        - クラス名と関連が明確なデータ
        - クラス名、データと関連が明確なメソッド
- 結合度
    - 複数クラスの依存度
        - 低い方がいい
            - 関数型


## 第 5 章 アーキテクチャ

- ３層アーキテクチャ
    - プレゼンテーション層
    - ビジネスロジック層
        - 低凝集になりやすい
            - 可読性・保守性の低下
    - データアクセス層
- なぜビジネスロジック層が肥大化するのか
    - 仕様を分解する
        - ユースケース図
            - タスクを登録する……
        - ドメイン知識（ルール/制約）
            - ユーザー
                - メールアドレスは重複登録できない
            - タスク
                - 期日は1日ずつ、3回まで延期できる
    - ドメイン知識をモデルに書く
        - ビジネスロジック層とデータアクセス層の結合度が高まる
            - 高結合
- レイヤードアーキテクチャ（エヴァンス本）
    - ビジネスロジック層を2つに分割する
        - ユースケース
            - アプリケーション層
        - ドメイン知識
            - ドメイン層
    - ドメイン層がインフラ層に依存してしまう
        - リポジトリに困る
- オニオンアーキテクチャ
    - 依存関係逆転の法則
        - ドメイン層がインフラ層のインターフェイスを持つ
            - 人がフライパンを「調理器具」というインターフェイスで持つ
                - RDBや外部サービスも使えるようになる
- ヘキサゴナルアーキテクチャ
    - 「ポートアンドアダプターアーキテクチャ」
        - 専用ポートとアダプターで外と通信する
            - インターフェイスを持つのと同じ
    - シンプル
        - 実装におすすめ
- クリーンアーキテクチャ
    - 基本的にはヘキサゴナルアーキテクチャの思想を引き継いでいる
        - 要素が多い
            - 一般的なWebアプリには取捨選択が必要
    - エンティティ層
        - 企業一般で適用できるビジネスルール
            - DDDの境界づけられたコンテキストの思想と合致しない
        - DDDではドメイン層の一部
- 境界づけられたコンテキスト
    - 基本
        - 1コンテキスト1アプリ
            - 販売コンテキスト
                - 商品、在庫、販売期間
            - 配送コンテキスト
                - 商品、配送先、配送状況
            - 商品でマッピング
        - パッケージで切る
    - 通信
        - 同期
            - REST APIなど
        - 非同期
            - AWS SQSなど


## 第 6 章 ドメイン層の実装

- ドメインオブジェクト
    - 「モノ」として表現する
        - エンティティ
            - 社員
                - 社員番号は123
                - IDは変わらない
            - 同一判定は識別子でする
        - 値オブジェクト
            - お金
                - 同じかどうかは文脈による
                    - 10円玉は区別しない
                    - コインコレクターには違うもの
                - 値オブジェクト自体が不変
                    - 所持金が10円が100円になっても10円玉は変わらない
            - 同一判定は属性値でする
    - 「コト」として表現する
        - ドメインイベント
- ドメインオブジェクトを使うところ
    - リポジトリ
        - 集約単位で永続化層へのアクセスを提供するもの
            - 親となる集約ルートを返す
            - 「リポジトリはListのように扱う」
                - 新規登録状態のユーザー、退会状態のユーザーをaddする
                    - 登録する、退会させるというメソッドは持たない
    - ファクトリー
    - ドメインサービス
        - モデルをオブジェクトとして表現すると無理があるもの
            - 集合に対する操作
                - メールアドレスの重複チェック
        - どうしてもエンティティや値オブジェクトにならない場合のみ使う
            - 手続き的になるから
- Q&A
    - なぜドメイン層にロジックを寄せるのか？
        - 「最も重要なドメイン層を、独立させて修正しやすくするため」
            - DBやフレームワークに依存させない
            - ユースケース層でテストを書く
    - ドメインオブジェクトはDBの該当テーブルのカラムと1対1となる？
        - そうとは限らない
            - ActiveRecord型のテーブルとオブジェクトの1対1に制限されない
    - ソートはどこでやる？
        - enumなどでソート順を引数指定できるようにする
            - order by はインフラ層でやる
    - ドメイン層は「どういう条件を指定したらどういうオブジェクトが取得できるか」にだけ関心がある
        - APIの呼び出し方
        - 取得結果をオブジェクトに詰める
            - インフラ層の関心ごと
    - オブジェクト間の参照関係
        - 集約内はインスタンス参照
        - 集約外はID参照
            - 違和感や無理があると気づいたら集約範囲を変更する


## 第 7 章 ユースケース (アプリケーション) 層の実装

- ユースケース
    - ドメイン層のメソッドを組み合わせてユースケースを実現する
    - タスクを延期する例
        - `findById`で取得する
        - `.postpone`で延期処理する
            - 延期の処理はドメイン層でやる
        - `save`する
- ユースケースからの戻り値
    - 戻り値クラスに詰めて返す
        - DTO（Data Transfer Object）
    - ドメインオブジェクトをそのまま返す
        - 数値の書式に変換などをドメイン層に書くことになる
            - 低凝集になる


## 第 8 章 CQRS

- Command Query Responsibility Segregation
    - コマンドクエリ責任分離
        - レイヤーとしてはユースケース層
- 複数の集約を操作するときの問題
    - 参照系と更新系を分ける
- デメリット
    - データが参照されている場所が追いにくくなる
    - 複雑になり理解にコストがかかる
- 部分導入できる


## 第 9 章 プレゼンテーション層の実装

- プレゼンテーション層
    - 「クライアントとアプリケーションの入出力の実現」