読書メモ：ドメイン駆動設計 モデリング/実装ガイド（前半）

会社で「[ドメイン駆動設計 モデリング/実装ガイド](https://techbookfest.org/product/6504762157563904)」の読書会に参加しました。
連休中に内容を見返すと、結構忘れていたので、内容をメモをすることで思い出そうと思います。

後半は[こちら](https://qiita.com/Y-Kanoh/items/de307f50acfe58984442)。

---------

# 第１章 DDD概要

DDDに取り組むにあたり、言葉の定義によって混乱することが多いので、ここで以下のように定義する。

## DDDとは
DDDとは、ソフトウェア開発手法の一つ。
ソフトウェアで問題解決をしようとしている対象（：ドメイン）をモデリングすることでソフトウェアの価値を高めようとする。

## モデルとは

問題解決のために、物事の特定の側面を抽象化したもの。

* ドメインモデル（モデル）：ドメインの問題を解決するためのモデル
* データモデル：データの永続化方法を決めるためのモデル

### モデルの例

モデルは、特定の事象を抽象化したもの。
例えば、履歴書の場合、以下の情報が読み取れる

* 名前
* 経歴
* 志望理由
* 顔写真
* 筆跡、筆圧
* 履歴書のメーカー
* 履歴書を書いた時の気持ち

これらの情報から、どのような情報をソフトウェアに取り込むかを決める過程が「抽象化」。
抽象化によってできた成果物が「モデル」。

## 良いモデル、良くないモデル

### 良いモデル
良いモデルとは、**問題を解決できるモデル**。
理解がしやすい、きれいに見えるなどは二の次。あくまでモデルは、問題解決のために、事象を抽象化したものである。

### 良くないモデル
モデルのステータスが追加できない、発生するイレギュラーケースに対応できないなど、実務での問題を解決できないモデルは、いかにテストがそろっていようと、バグが少なくても、評価されない。

## 良いモデルを作るには

### ドメインエキスパートと会話をする
そのドメインに詳しい人（：ドメインエキスパート）と会話をし、モデルに知識を反映することが大事。
ただし、ドメインエキスパートは開発のエキスパートではないため、次の視点も必要になる。

### 運用して得られた発見をモデルに還元する
運用して発見した改善点は、細かく追加していく。
モデルは最初から完成せず、徐々に改善していくもの。
（そのため、コードは頻繁な変更に耐えられる作りにする必要がある。）

## DDDの問題解決のアプローチ
以下のアプローチを行うことが重要。

* モデルを継続的に改善
* モデルを継続的にソフトウェアに反映

### モデルの継続的な改善
ドメインについての理解を深め、モデルを継続的に改善する。

* ユビキタス言語
    * 発見したモデルに関する物事を、プロダクトに関わる全ての場所において同じ言葉で表現すること
    * ビジネスサイドの人とも共有する
    * 会話でも、ドキュメントでも、コードでも、同じ言葉を使う
* 境界づけられたコンテキスト
    * あるモデルを同じ意味で使い続ける範囲のこと
    * 第３章参照のこと

### モデルを継続的にソフトウェアに反映
* モデルを直接表現するコードへの反映
    * 極力モデルとコードを近づける
* モデル表現を隔離するアーキテクチャ
    * モデルを表現するコード意外にもUIやDBとのやりとりを表現するコードが存在する
    * モデル表現と、それ以外を表現するコードが混ざるとややこしい
    * モデルの表現を行うコードを「ドメイン層」として隔離する

## 取り組む上で重要な考え方

* 課題ドリブン
    * 意思決定の際には、課題を明確にして解決できるかを考える
    * ルールより課題解決ができるかを重視する
* 小さく初めて、小さく失敗する
    * モデリングに1週間かけるより小さく実装する方が良い
    * 小さい部分から始めて、少しずつ成功体験を積み上げる



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

## ドメインモデリング

DDDでは、モデリングの方法や、アウトプット形式が迷うポイント。
今回は比較的シンプルで、小さく始めて効果を出しやすいユースケース図とドメインモデル図を利用する。

## ユースケース図

ユースケース図は、「ユーザの要求に対するシステムの振る舞いを定義する図」。
ユーザを定義し、棒人間で表して、ユーザが行う動作を吹き出しで「〇〇を××する」といった形式で書き出す。
さらに、今回のモデリングにおけるスコープも、図中に明記する。

ユースケースを書き出すことで、どのようなモデルを作れば良いか判断できるようにする。
ユースケースが具体化されていないと、モデルで解決すべき問題点が明確にならず、良いモデルとそうでないモデルの区別が付かなくなってしまう。

また、モデリングにおけるスコープを明記することで、議論の発散を防ぐ。

## ドメインモデル図

ドメインモデル図は、簡略化したクラス図。

* 書くこと
    * オブジェクトの代表的な属性
    * 「ルール」、「制約（ドメイン知識）」を示した吹き出し
        * 箇条書きなど
        * 状態遷移図などでもOK
    * オブジェクト同士の関係
        * 集約内の参照は「-◆」で表す（インスタンス参照）
        * 集約外の参照は「→」で表す（ID参照）
    * 多重度の定義
    * 集約範囲の定義
    * 具体例などもあれば
* 書かないこと
    * メソッド

（ここは、書籍のP24にある図を見た方がわかりやすい。）

## ドメインモデルの実装

作成したドメインモデルを、コードに落とす。

### ドメインモデル貧血症
ドメインモデルを実装するためのオブジェクトでありながら、ドメイン知識をほぼ持っていないオブジェクトのこと。
書籍ではドメイン貧血症のコードをリファクタリングする。

### アーキテクチャ
まず、全体のアーキテクチャを決定する。
レイヤーを定義して、各レイヤーに実装するクラスの方針を定義する。
（ここでは、オニオンアーキテクチャを採用）
詳しくは第５章参照。

### ドメイン貧血症のコード
（このメモを書いている私の都合でPHPで書きます...）

```php:❌ドメイン貧血症のコード
public class Task
{
   private id;
   private taskStatus
   private name;
   private duDate;
   private postponeCount;

   // 全ての属性には、publicなsetterとgetterが存在する
}
```

* 問題点1：不整合なデータをいくらでも作り出せる
    * 全てのsetterがpublicであるため、自由にデータを作り出せてしまう
* 仕様の把握のためには、多くのクラスやコードを辿る必要がある
    * ステータスや期日変更にはどんなパターンがあるかこのクラスだけではわからない

### ドメインモデル知識を表現した実装

ドメイン貧血症を防ぐためには、以下に注意する
 * ドメイン知識はドメイン層で実装する
    * ユースケース層で実装しない
    * ユースケースには「なにをしたいか（What）」のみが記載されるようにする
* 想定外の操作が行われないようにする（例えばpublicなsetterは作成しない）

## ドメイン層オブジェクト設計の基本方針
以下の2点に従う。

* ドメインモデルの知識を対応するオブジェクトに書く
* 常に正しいインスタンスしか存在させない
    * 生成条件を強制する（なにもないコンストラクタは実装しない）
    * 内部状態の変更を強制する（ミューテーションを起こすメソッドを正しく実装し公開する）

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

## 集約
集約とは、「必ず守りたい強い整合性を持ったオブジェクトのまとまり」のこと。
以下の2ルールにしたがって定義する。

* 強い整合性確保が必要なものを集約にする
    * オブジェクトの属性が他のオブジェクトに強く影響する場合など
* トランザクションを必ず1つの集約にする
    * 整合性を守るため、集約単位でリポジトリからデータを取り出したり、更新したりする

### 集約ルートとは
各集約には、ルートとなるオブジェクトを1つ決め、集約ルートと呼ぶ。

## 集約の決め方
* 集約の境界は、機械的な決め方は存在しない
* 整合性確保の重要性を加味して決める
    * 複数集合間で整合性を確保しないわけではないので、整合性確保が必要だから必ず集合にする必要はない
* トランザクション範囲の適切さも加味する
    * 大きすぎる集合だと、DBへ反映するときに負荷がかかる

## 境界づけられたコンテキスト
同じシステム内でも、1つの事象をすべて1モデルで表すのには無理がある。
そのため、適切な範囲でモデルがカバーする範囲を分割する。
このモデルがカバーする範囲のことを「コンテキスト」と呼ぶ。
（モデルを別にして、コンテキストが変わった時は情報を詰め替えるイメージ？？）


# 第４章 設計の基本原則
## 凝縮度

凝縮度とは、1クラスでの「責務、データ、振る舞いの関連の強さ」の尺度。
なんでもできるクラスを作るのではなく、責務に沿った内容が集約されているかどうか。
一般的に**高凝縮**がいいとされる。

責務を明確にするため、「このクラスは何をするクラスか」を明確にすべき。

* ❌ 低凝縮に陥る例
    * クラス名自体が責務を曖昧にしている
    * クラス名と関連が明確でないデータ名
    * 目的語が欠如したメソッド名（「なにを」インクリメントするのか、など）
    * 保持しているデータと関係ないロジック

## 結合度
結合度とは、複数のクラス同士が依存している度合い。
一般的に、**低結合**が良いとされている。
結合度が高いと、依存先クラスの修正による影響を受けやすくなる。
また、__実現したい処理を、多くのクラスを組み合わせないと実現できない場合は、すでに結合度が高くなっていると考えられる。__

## 高凝縮・低結合で得られるメリット

* コードを理解しやすくなる
* コードの修正/拡張がやりやすくなる
* バグが含まれにくくなる
* コードを再利用しやすくなる

* テストがしやすくなる

# 第５章 アーキテクチャ

DDDで提唱されているアーキテクチャの解説。

## 3層アーキテクチャ
### 概要
<img width="600px" alt="3層アーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176262/b9df1bb6-e166-fc65-904c-aadfb1e304fe.png">



### デメリット
以下の理由で可読性や保守性が下がる。

* ビジネスロジックが膨らむ。（低凝縮になる）
    * ビジネスロジックがユースケースとドメイン知識両方を担っているため
* ビジネスロジック層とデータアクセス層が高結合になってしまう
    * ドメイン知識がビジネスロジック層とデータアクセス層にあるため


## レイヤードアーキテクチャ
### 概要
3層アーキテクチャのビジネスロジック層を2つに分けたもの。

<img width="800px" alt="レイヤードアーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176262/c31ae97d-f775-6658-ea05-f16246845780.png">


### メリット
* レイヤーごとに責務がはっきりし、可読性、保守性が上がる（高凝縮になる）。

### デメリット
* ドメイン層がインフラ層に依存してしまう
    * ドメイン層にリポジトリをおく場合、DBやORマッパーに依存した実装になる

モデルを継続的に改善するためには、ドメイン層はインフラ技術に依存すべきではない。

## オニオンアーキテクチャ
### 概要

レイヤードアーキテクチャから、ドメイン層とインフラ層の依存関係を逆転させたもの。
図中の白矢印は、インターフェイスの実装を示す。

<img width="600px" alt="オニオンアーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176262/357d2eda-7919-333f-cf09-0995df3bf25a.png">

名前の通り、丸型で表すこともできる。
この場合、依存関係は「外側から内側」のみ許される。
<img width="450px" alt="オニオンアーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176262/a1697151-5ce9-3202-2acb-4b08e96bdb41.png">

プレゼンテーション層、インフラ層、テストは、それぞれアダプターを通して外部と通信する。
（スマホアプリ、ブラウザ、RDB、ファイル、外部サービス、結合テスト など）

* ドメイン層
    * ドメイン知識の表現
    * ドメイン層の独立で他の層への依存を持たせない
    * 整合性が保てるメソッドのみ外部に公開する
    * 実装するクラスは以下の通り
        * エンティティ
        * 値オブジェクト
        * ドメインイベント
        * リポジトリインターフェイス
        * ドメインサービスファクトリー
* ユースケース層
    * ドメイン層のメソッドを使ってユースケースを実現する
    * 特定のクライアントに依存させない （クライアントに合わせるのはプレゼンテーション層）
    * 実装するクラスは以下の通り
        * ユースケースクラス
        * プレゼンテーション層との入出力定義クラス
* プレゼンテーション層
    * アプリケーション外部との入出力を実現
    * 実装するクラスは以下の通り
        * コントローラ
        * アプリケーション外部との入出力を実現するクラス
* インフラ層
    * 下位レイヤーのインターフェイスを実装する
    * 実装するクラスは以下の通り
        * リポジトリ実装クラス

### メリット
* ドメイン層を特定の技術に依存させずに済む

## ヘキサゴナルアーキテクチャ
### 概要
アプリケーションが外の世界と通信する際には、専用のポートとアダプターを作成して通信させるアーキテクチャ。
レイヤーの分け方が異なるが、基本的にはオニオンアーキテクチャの思想を踏襲したもの。
（別に六角形である意味はない。）

## クリーンアーキテクチャ
### 概要
オニオンやヘキサゴナル、その他のアーキテクチャを受けて概念を結合しようとしたアーキテクチャ。

なお、筆者のおすすめはシンプルさと元々のDDDが目指す思想との差異から「オニオンアーキテクチャ」だそう。

## 境界づけられたコンテキストの実装
* 1アプリケーション1コンテキストとするのが簡単
    * コンテキストごとのマイクロサービスとなる
    * ただし実装コストは高くなる
* パッケージで区切ってしまう
    * 逆に複雑になる時もある
    * パッケージで区切る方法で始めて、後にサービスを分割する方法もアリ


後半は[こちら](https://qiita.com/Y-Kanoh/items/de307f50acfe58984442)。