ドメインモデルとは(「現場で役立つシステム設計の原則」より)

[現場で役立つシステム設計の原則](https://www.amazon.co.jp/dp/B073GSDBGT) を読んで、
その内容をチームに伝えるようにまとめたメモです

# ドメインモデルとは
業務で扱う(最小)単位でデータとロジックをひとまとめにして整理する技法

# ドメインモデルが実現する世界
- どこに何のロジックが書いてあるか、(ソースを見るだけで)わかる
- 改修しやすいプログラム

# ドメインモデルで解決したい問題
**どこに何のロジックが書いてあるかわからない問題**
- わからないから適当に書く→適当に書くからわからない → わからないから・・・
- ある修正をしようと思っても、どこまでが影響範囲かわからない
    - 使用している箇所全grepして調査
- 同じような処理、分岐が重複してしまう

これを解決したい。これだけを考える。

## なぜ(今までのシステムは)改修は難しいのか
- 機能クラスと、データクラスをわけてしまうから
    - そのデータクラスを呼べる箇所 = 業務ロジックがかけてしまう
        - どこになにが書いてあるかわからなくなる
- 共通クラス、Utilクラスを作ってしまうから
    - 誰でも呼べる = そのクラスを使って何をやっているのか、影響範囲が大きくなる
    - 微妙にニーズが違うせいで、関係ない分岐まで混ざってしまう
    - 肥大化して結局どこに何が書いてあるかわからなくなる
    - 重複コードが生まれる
    
![Untitled Diagram (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263190/2b22bf41-6520-2b2b-4117-42cdd16b5e53.jpeg)


## じゃあどうすればいいのか
- データとロジックを一体化することで、業務ロジックを整理する
- 三層+ドメインモデルの構成にする
- すべての業務ロジックをドメインモデルに集める
- 他の層は、判断、加工、計算はドメインモデルに任せる

![Untitled Diagram (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263190/e860c595-8fd9-2b2f-3d9f-fdb450e81381.png)

 
## 実装する上でのポイント

### 値オブジェクトを使う
なにかの料金クラスがあるとして、そのインスタンス変数をInteger moneyとしない。
専用の型(クラスやインターフェース)で、不適切な値は混入させない。
値オブジェクトを用意すると、コードが安定する。

### 値オブジェクトは不変にする
値が書き換わるときは、別オブジェクトをnewして返すこと
内部で値を書き換えてしまうのは副作用が大きすぎる。
コードを読むときに、この変数はどこで値が変わっているか、を気にしないといけなくなる
→改修の手間(調査)が一気に面倒になる

変数の値は変わらない。だからコードが安定する。

悪い例

```
Money price = new Money(3000);

price.setValue(2000);  // × 値は書き換えている
price = new Money(4000); // 1つの変数に別の値を代入している
```

良い例

```
Money basePrice = new Money(3000);
Money discount = basePrice.Minus(1000); // minusメソッドは、内部で引数を元に計算した値をnewして返す

Money option = new Money(2000); // 変数は上書きしない
```





### クラスを使う側にロジックを一切書かせない
従業員 classがあった場合

```
Employee class {
    // 入社日時 (データ
   JoinDate joinDate;

    // 生年月日 (データ
   Birthday birthday;

    // 職種 (データ
   JobType jobType;

    // 年齢取得メソッド(ロジック
   Integer Age () {
        // 生年月日から年齢を返す
   }

    // エンジニア判定メソッド
    boolean isEngineer () {
        // jobTypeがエンジニアならtrueを返す
    }

    // クリエイター判定メソッド
    boolean isCreator () {
        // jobTypeがエンジニアorデザイナならtrueを返す
    }    
}
```

getterは基本的に使わない
→利用するクラスはgetしたデータでなにをしようとしている？
→データを使って、加工/判断/計算をする場合は、ドメインオブジェクトに必ず閉じ込める
    →そのデータを使うロジックは、ドメインオブジェクトを見ればわかるようにする

利用側のクラスが↓こんな感じで判断してはいけない

```
if (employee.getJobType() == エンジニア) {
    XX処理
}
```

### クラスをデータの入れ物として考えない
あるクラスを「データの入れ物」と考えてしまうと、そのクラスからデータをgetして、自分でロジックを書くのが当たり前になってしまう

データを持つクラスのメソッドを「ロジックの置き場所」と考えれば、そのクラスが、判断、加工、計算までやってくれる、便利な部品になる

### メソッドの書かれている場所の妥当性を考える
インスタンス変数を使わないメソッドは、そのクラスのメソッドとして不適切
(インスタンス変数でない引数だけを使うメソッドはだめ→どこに何が書いてあるかわからなくなる原因)
→つまりそれは、その処理で使っているクラスのメソッドであるべき(データの近くにロジックを置く)

### その他実装のポイント
- 短いメソッド、小さいクラスを使って、コードを整理する
- 値オブジェクトを作る
- クラス名や、メソッド名を業務の用語と一致させる


## クラスがたくさんできる、、、？
できます。
クラスがたくさんできるので、それをパッケージ(ディレクトリ)に分けて、管理しましょう。
パッケージが増えてきたらサブパッケージで管理。

階層が増えてきたり、ある階層には1クラスしかない、とかもあると思う。
けど、それでもよい。
大事なのは、どこに何があるかをわかりやすくすること。

その階層を見るだけで、どこに何が書かれているかわかるようにする。

また、パッケージをわけ、それの使う、使われるの依存関係をはっきりすることで、ロジックの重複を避けることができる
知らないでいいことを知りすぎたり、責任を誰が持つのかを明確にする

# 補足
- 設計初期の段階から、完璧なドメインモデルは作れない
    - むしろ作りながら、育てていく(リファクタしまくろう

## ドメインモデルの設計が難しいと言われるのはなぜか
-  オブジェクト指向の経験不足
  - 手続き型の発想から抜け出せていない。
  - データとロジックを1つのクラスにまとめる考えのメリットが感覚的にわかっていない
-  要件定義、分析のやり方が間違っている


# まとめ
- 変更が大変になるのは、データクラスと機能クラスをわけるから
- データクラスを使うと、業務ロジックの重複が増える
- 全体の見通しを良くするために、データとロジックを一体にする設計を徹底する
- 業務ロジックは、業務データの近くにまとめる(ドメインオブジェクト
- ドメインモデルは、画面やDBの都合から独立させる
- 他の層は、業務的な判断/加工/計算ロジックをドメインモデルに任せることでシンプルになる


# 謝辞
この記事を書いたところ、この本「現場で役立つシステム設計の原則」の著者である増田さんからお褒めの言葉を頂きました。
https://twitter.com/masuda220/status/1191627212903964673
こちらこそ大変参考にさせて頂いております。素晴らしい本を書いてくださりありがとうございます。