「ドメイン駆動設計入門」を読んで_その2

## この記事は

[前回記事 - 「ドメイン駆動設計入門」を読んで](https://qiita.com/t13801206/items/90a41f8ea880f30425b9)のつづき
[リポジトリ](#リポジトリ)と[アプリケーションサービス](#アプリケーションサービス)についてまとめる

つづき：[「ドメイン駆動設計入門」を読んで_その3](https://qiita.com/t13801206/items/e0fc016070ab9a93e624)

## リポジトリ

リポジトリは、オブジェクト（データ）を永続化し、また再構築する

```plantuml
entity ドメイン
control リポジトリ
database データストア

ドメイン -> リポジトリ : 永続化または\n再構築を依頼
リポジトリ -> データストア : 永続化または\n再構築
データストア -> リポジトリ : 再構築したオブジェクトや\n永続化の完了信号
リポジトリ -> ドメイン : 再構築したオブジェクトや\n永続化の完了信号


note top of ドメイン : 値オブジェクト、エンティティ、ドメインサービス
note bottom of ドメイン : ドメインはデータストアの実態を知らなくてよい
note top of データストア : RDB、NoSQL、ファイルなど
```

大事なのは、**ドメインがデータストアの実態を知らなくてよい**ということ
ドメインはリポジトリに依頼するだけでよい

## ドメインサービスの見通しをよくするリポジトリ

値オブジェクトやエンティティの補助となるドメインサービス

たとえば、あるエンティティがすでに存在するか確認するためには
データストアへの問い合わせを記述せねばならず、ドメインサービスのコード量が増える

### 見通しをよくするための方法

インターフェイスをつかって任意のリポジトリを注入することで
データストアの実態によらず、すっきり簡潔に書ける

```cs
class SomeDomainService
{
    private IRepo repo;

    public SomeDomainService(IRepo repo) // コンストラクタインジェクション
    {
        this.repo = repo;
    }

    public void OtherMethod()
    {
        // this.repo を使った何らかの処理
    }
}
```

### リポジトリのインターフェイス

```cs
interface IRepo
{
    Obj Find(ObjName name)
    void Save(Obj obj)
    Option<Obj> Find(ObjName name)

    // リポジトリの責務は、永続化と再構築である
    bool Exist(ObjId id) // <- オブジェクトの有無を確認するのは責務範囲外ともいえる
}
```

### サンプルコードの実現方法

データ永続化の手段が３つ用意されている

> インメモリで動作するモジュール
> SQL 文を利用したモジュール
> Entity Framework を用いたモジュール

JSONファイルの一部を書き換えることで、コンフィグがスイッチするみたいだけど
コンフィグの中のセットアップ処理についてはよくわからない・・

```json
  "Dependency": {
    "setup": "EFDependencySetup"
  },
```

https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal#dependency-injection

https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal/WebApplication/Config/Dependency


## アプリケーションサービス

> アプリケーションサービスはドメインオブジェクトを協調させてユースケースを実現します （本書 P113）

ドメインモデルをドメインオブジェクトで表現できたら
次は目の前にある実問題を解決するためにアプリケーションサービスを構築する

### ドメインサービスのおさらい

似たような言葉ですでにドメインサービスがある
ドメインサービスは**ドメインオブジェクトに対するサービス**である


- 値オブジェクトやエンティティがもつと不自然になるふるまいを代行する
  - まずは値オブジェクトやエンティティにふるいまいをもたせることを検討する
- 自身のふるまいを変更するような状態をもたない

[前回記事に詳細あり](https://qiita.com/t13801206/items/90a41f8ea880f30425b9)

### ユースケースを組み立てる

QiitaなどSNSでは以下のようなユースケースが想定される

```plantuml
left to right direction
actor User as u
package CRUD {
  usecase "C)あたらしくアカウントをつくりたい！" as UC1
  usecase "R)登録情報を確認したい！" as UC2
  usecase "U)アカウント画像を変えたい！" as UC3
  usecase "D)退会したい！" as UC4
}
u --> UC1
u --> UC2
u --> UC3
u --> UC4
```

### ドメインオブジェクトを用意する

GitHubに本書のリポジトリがあるので、そちらを参考にドメインオブジェクト群をつくる

- [ユーザーに関するドメインモデルの例](https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal/SnsApplication/Domain/Models/Users)
- [ユーザーに関するドメインサービスの例](https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal/SnsApplication/Domain/Services)

ドメインオブジェクトは材料でしかない
かき集めたこれらの材料を利用するのはアプリケーションサービスである

### アプリケーションサービスをつくる

[UserApplicationService.cs](https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal/SnsApplication/Application/Users)に`Register`や`Delete`など、ユースケースに沿ったメソッドが用意されている
そしてそれらのメソッドはドメインオブジェクトのインスタンスである`userService`や`userRepository`などを利用している

### ドメインオブジクトの公開範囲

「登録者情報を確認したい！」の文言のとおりに、そのままドメインオブジェクトであるユーザーインスタンスを戻り値で渡した場合、予期せぬふるまいまでされてしまう危険性がある
（単純に登録情報を閲覧するためだけにゲットした`user`の`chageName()`メソッドが呼ばれた場合など）

**DTO（Data Transfer Object）** を使うことで、ドメインオブジェクトを直接渡さずに済ませられる

リポジトリから見つけたユーザー（ドメインオジェクト）を`UserData`（DTO)に詰め替えて、さらに`UserGetResult`に詰め替えて戻り値としている

こうすることで、必要最低限の情報のみを外部へ公開している

```cs
// UserApplicationServiceのGetメソッド
var user = userRepository.Find(id);
var data = new UserData(user);
return new UserGetResult(data);
```

## ユーザー情報を更新するコマンド

登録情報を更新する`Update`処理を考える

リリース当初は`Update(アカウント名)`だけでよかったかもしれない
数か月後、`Update(アカウント名、メールアドレス、性別)`となり
数年後、`Update(アカウント名、・・・たくさんの引数・・・)`となる


```plantuml
left to right direction
actor User as u
package リリース当初 {
  usecase "アカウント名を変更したい！" as UC2
}
u --> UC2
package 数か月後・・・ {
  usecase "アカウント名を変更したい！" as UC3
  usecase "メールアドレスを変更したい！" as UC4
  usecase "性別を変更したい！" as UC5
}
u --> UC3
u --> UC4
u --> UC5
```

将来引数が増えていくやもしれぬ処理については
ファサードとなるコマンドを導入することで対処する

`UpdateCommand`のコンストラクタに変更したいパラメータを渡す
変更したくないパラメータについては`null`のままとしておく

```cs
// コマンドのコンストラクに
UpdateCommand(arg1 = null, arg2 = null, arg3 =null)
{

}

// 更新処理 
public void Update(UserUpdateCommand command)
{
  if(command.arg1 != null)
  {
    // 更新処理
  }
}
```

### 登録処理と退会処理をわける

`UserApplicationService`はもっと細分化できる

登録や更新では、ドメインサービス`userService`に重複確認を行う
一方、退会`Delete`では重複確認の必要はない

凝集度の指標として、**そのクラスのメソッドがすべてのインスタンス変数を使っていること**が目安になる

`UserApplicationService`を`UserRegisterService`と`UserDeleteService`にわけることで、より高い凝集度のクラスをつくることができる

```plantuml
(もともとの\nUserApplicationService) as uas
(登録処理\nUserRegisterService) as urs
(退会処理\nUserDeleteService) as uds
(重複確認\nuserService) as us
(永続化\nuserRepository) as ur

uas --> urs : わける
uas --> uds : わける
urs --> us : 登録前に重複を確認する
urs --> ur : リポジトリにレコードを追加する
uds --> ur : リポジトリからレコードを削除する
```

https://twitter.com/masuda220/status/1393495134738681862?s=20

#### アプリケーションサービスのインターフェイス

```cs
interface IUserSomeService
{
  void Handle(SomeCommand command)
}
```

### サービスとはなにか

- サービスは自身のためのふるまいをもたない
- サービスはものごとではなく、活動や行動である
- ドメインサービスとアプリケーションサービスの本質は同じ
  - **ドメインの知識を表現したものがドメインサービス**
  - **利用者の問題を解決するものがアプリケーションサービス**

### アプリケーションサービスのまとめ

とにかくドメインの知識がアプリケーションサービスに漏れ出ないようにすること！