「ドメイン駆動設計入門」を読んで_その7(最後)

## この記事は

「ドメイン駆動設計入門」を読んでまとめている第7弾

前回記事は[こちら](https://qiita.com/t13801206/items/8409752cbe6b75da5639)

今回は[アーキテクチャ](#アーキテクチャ)と[軽量DDD](#軽量dddにならないために)と[境界付けられたコンテキスト](#境界付けられたコンテキスト)についてまとめる
末尾に[ソリューション構成](#ソリューション構成)を書く

まとめている過程でみつけた[とても分かりやすい記事](https://qiita.com/little_hand_s/items/ebb4284afeea0e8cc752)（Qiita内リンク）

## アーキテクチャ

> アーキテクチャは知識を記述すべき箇所を示す方針(P317)

ドメインからルールが漏れ出すと、変更箇所が点在し、バグの原因となる
ルールがサービスに漏れ出ることを貧血症と呼ぶ

貧血症を予防する力を養うために、アーキテクチャを学ぶ

- [レイヤードアーキテクチャ](#レイヤードアーキテクチャ)
- [ヘキサゴナルアーキテクチャ](#ヘキサゴナルアーキテクチャ)
- [クリーンアーキテクチャ](#クリーンアーキテクチャ)

### 悪い例「利口なUI」

ロジックを知るUIを利口なUIと呼ぶ
利口なUIは**アンチパターン**である

#### UIの責務

UIの責務は**入力(の解釈)と表示**である
UIは利用者からの入力を受け取り、ルールに基づいて出た結果を表示することだけが責務である

#### ECサイトの例

たとえばAmazonなどのECサイトのUIは、買う／買ったものの注文金額を表示する
**注文金額を算出する方法はルール**であり、**ルールはUIに書かれるべきではない**

"注文金額"はECサイト内の様々な場面（画面）で表示される
それらの表示内容（注文金額）をUIが計算してはいけない
**（ではどこに書くべきか？を定めるのが各アーキテクャはであり後述される）**

- 注文確認
  - 注文確定ボタンを押す画面
- 注文履歴一覧
  - 過去に注文したリストが一覧で見れる画面
- 注文履歴詳細確認
  - 注文した内容の詳細が見れる画面（何を何個買った）

```plantuml
class サーバー
{
    注文金額計算ロジック
}
注文確認画面 --> サーバー : 注文金額問い合わせ
注文履歴一覧画面 --> サーバー : 注文金額問い合わせ
注文履歴詳細確認画面 --> サーバー : 注文金額問い合わせ
```

### レイヤードアーキテクチャ

[本書で扱ってきたサンプル](https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal)はレイヤードアーキテクチャである

- **プレゼンテーション（UI）**
  - ユーザーインターフェースとアプリケーションを結びつける
  - **表示と入力の解釈**をする
  - WebもCLIも入力のひとつ
    - MVCフレームワークのコントローラなど
- **アプリケーション**
  - ユースケースを実現する進行役
  - ドメインを取りまとめる
    - ドメインオブジェクトの直接のクライアントである
  - ルールは記述しない（ドメインに書く）
- **ドメイン**
  - 問題を解決するルールや知識を書く
  - **ファクトリやリポジトリのインターフェースもここに書く**
    - [まとめその3](https://qiita.com/t13801206/items/e0fc016070ab9a93e624)で依存関係のコントロールについて触れた
- **インフラ**
  - アプリケーションのための**メッセージ送信**
  - **ドメインの永続化・再構築**

### ヘキサゴナルアーキテクチャ

ゲーム機をTVやコントローラにつなげるように、つながれば動くイメージ
TV出力つながった先は液晶TVでもタブレット出力でもよい
コントローラは純正品でもサードパーティーのジョイコンでもよい
つながれば動く
ポートアンドアダプタとも呼ばれる

入力をプライマリポート／プライマリアダプタ、
外部に対してふるまうことをセカンダリポート／セカンダリアダプタと呼ぶ

```cs
class UserAppService
{
    // セカンダリポート
    private readonly IUserRepository repository

    // Updateメソッドはプライマリポート
    // Updateメソッドを呼ぶクライアントはプライマリアダプタ
    public void Update(UpdateCommand command)
    {

        // 処理がセカンダリアダプタにうつる
        repository.Save(user)
    }
}
```

レイヤードアーキテクチャは層をわけるところまでしか言及していない一方、
ヘキサゴナルアーキテクチャはインターフェイスの考えも入っている
・・・と書いてみても、正直よく理解できていない

今日においては、インターフェイスをつかって依存関係の逆転をすることは当たり前に行われているので、両者に違いはないとのこと。。。

### クリーンアーキテクチャ

ヘキサゴナルアーキテクチャはアダプタとポートがつけ外し可能ですよ、まで。
クリーンアーキテクチャは具体的な実装方法が明示されている（よく見る円の図の右下に書かれている）

円の中心にはドメインのルールが記述されたオブジェクトがあり、
詳細は円の外側に追いやられている
依存の矢印は円の中心に向かっている

### アーキテクチャまとめ

いずれにせよ**ドメインを隔離**し、ドメインに対して依存すればよい
ドメインに重要なルールをおまかせする

そしてアーキテクチャを考えるときは全体を見ず、局所的に関係性を考える
あれもこれもではなく、近くのオブジェクトと話すようにする
そうすれば矢印は遠くまでいかず、依存も最小限に収められる

アーキテクチャは方針であり「何をどこに書くか」の指針である
ソフトウェア特有の事情からドメインオブジェクトをうまく隔離できるアーキテクチャを選択する

## 軽量DDDにならないために

> トンカチを手にすると目の前のものが釘に見えて仕方なくなる(P344)

ここまでに学んだ値オブジェトなどの概念は、あくまで道具でありパターンである

一方、ソフトウェアの目的は課題を解決することである

パターンを使いたいがために、課題解決への道を誤ることがあってはならない

トンカチを持った子どもよろしく、DDDを学んだ開発者がすべての課題に対してDDDを当てはめようとすること（パターンに固執すること）を軽量DDDと呼ぶ

### ドメインエキスパートと対話をする

課題は現場にある
課題を知るのはドメインエキスパート（そこで働く人）である
ドメインエキスパートの言葉を鵜吞みにせず、対話を通じて課題の解像度を上げていく
本当に解決すべきことは、最初の課題とはずれたところにあるかもしれない

```plantuml
現実世界の課題 --> ドメインモデル : 対話\nどの知識がシステムに役立つか？\nドメインの言葉を知る
ドメインモデル --> コード : ユビキタス言語をコードに使う
ドメインモデル <-- コード : 無理なモデリングがあることに気づいたら\nモデルを修正する
現実世界の課題 <-- ドメインモデル : 対話\n気づきのフィードバック
```

#### ユビキタス言語をコードに使う

ドメインエキスパートが「名前の変更をしたい」と表現しているところは
`UpdateName`より`ChangeName`をつかうことで
翻訳のコスト、解釈のすれ違いを減らす

```cs
class User
{
    // good 名前の変更：ドメインにとって自然な表現
    void ChangeName(UserName name)
    {
        // ...
    }

    // bad 名前の更新：DBを意識した技術的な表現
    void UpdateName(UserName name)
    {
        // ...
    }
}
```

## 境界付けられたコンテキスト

境界付けられたコンテキスト＝ドメインの国境のようなもの

同じ言葉でも意味するものが異なることがある
あるいは同じ言葉でも区別したほうがいいときがある

その場合は、意味別にパッケージをわける


```plantuml
Title: パッケージをわけない場合
package Users
{
    class "User" as u1
    {
        + UserId
        - UserName
        + ChageName()
        + IsValidPassword()
    }
}
```

```plantuml
Title: 意味ごとにパッケージをわけた場合
package Core.Model.Users
{
    class "User" as u1
    {
        + UserId
        - UserName
        + ChageName()
    }
}
package Authenticate.Model.Users
{
    class "User" as u2
    {
        + UserId
        + IsValidPassword()
    }
}
```

文脈ごとにパッケージをわけることは、システムが大きくなるほど効果的

### コンテキストマップ

もとは１つであった`User`を分割することで、ドメイン全体にわたるユーザーを把握することが難しくなる
また、ある文脈の`User`を変更したときに、ほかの文脈も変更しなければならない場合がでてくる

```plantuml
Title: コンテキストマップ
package Core.Model.Users
{
    class "User" as u1
    {
        +UserId
    }
    class Circle
}
package Authenticate.Model.Users
{
    class "User" as u2
    {
        +UserId
    }
}
u1 -- u2
Circle - u1
```

たとえば、識別子を`UserId`から`UserMailAddress`に変更した場合などは、関連するパッケージを開発する部隊にその旨を周知する必要がある

#### テストが最後の砦

修正内容が伝達されなかったときにソフトウェアを守る最後の砦がテストである

## ソリューション構成

Visual Sudio でコードを書き出すときにまずやるのがフォルダ構成を考えること

### ドメイン

- Core
  - Models
    - Users
      - **User**: 集約ルート
      - UserId
      - UserName
      - **IUserRepository**
      - **IUserFactory**
    - Circles
  - Services
    - UserService: 重複確認など
  - Shared
    - ISpecification
- Authenticate
  - Models
    - Users
      - **User**
  - Services
    - UserService: パスワード認証など

### アプリケーション

- Application
  - Users
    - **UserApplicationService**
    - UserGetCommand
    - UserGetResult
    - UserCreateCommand
    - **UserData**: データ転送用オブジェクト（ドメインオブジェクトのメソッドを安易に呼び出さないための措置）
  - Circles

### インフラ

- EF
  - Users
    - **EFUserRepository**: ドメインにあるインターフェイスを実装する
    - **EFUserFactory**: ドメインにあるインターフェイスを実装する
  - Circles
- InMemory

#### ドメインとアプリケーションを同じプロジェクトにする

ドメインのクライアントをアプリケーションに限定するために、その２つだけを同一プロジェクトにまとめる方法もある

- プロジェクト
  - Application
  - Domain
- インフラ
- プレゼンテーション

## 全体のまとめ

ドメイン駆動について少しは理解が深まった

ふだん使っている C# WPF (Prism Template)ではどう区分けするか実際にやってみようと思う

あと、Qiita Jobs のアンケート答えたら Amazon 5,000円券もらえたので、本家のエバンス本を買いたい・・・と思ったけど5,000円じゃ買えなかった orz