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

## この記事は

「ドメイン駆動設計入門」を読んで内容をまとめているシリーズの第4弾

[ファクトリ](#ファクトリ)と[データ整合性](#データの整合性を保つ)についてまとめる

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

## ファクトリ

> オブジェクト生成の責務はモデルを表現するオブジェクトには相応しくない (P205)

> 複雑な道具は得てしてその生成過程も複雑 (P206)

ドメインオブジェクトが役割を全うするために、**ファクトリは煩雑なオブジェクト生成処理を代行する**
クライアントは道具を使いたいだけ＝＝道具の生成過程には興味がない

ましてや**ドメインオブジェクトのコンストラクター内で、低レベルなデータベースに接続する**、、なんてことは**できればやめたほうがいい**

### ファクトリのインターフェイス

シンプルな生成メソッドだけ用意する

インターフェイスを用意することで、本番とテストで実装を分けることができる

- 本番：データベースにアクセスしてエンティティIDを採番する
- テスト：ユーザーインスタンスを生成するたびに、内部的に `id++` で連番を生成する

```cs
public interface IUserFactory
{
    User Create(UserName name);
}
```

### ファクトリが存在することをわかってもらう

[このようなパッケージ分け](https://github.com/nrslib/itddd/tree/master/Layered_UsingInternal/SnsApplication/Domain/Models/Users)にすることで、`Users.cs`の使用者は**近くにそのファクトリが存在することに気づく**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/228adf3d-c038-ccfe-cc10-f4103a4d34a0.png)

### ファクトリをつかう指標

**「コンストラクター内でほかのオブジェクトを生成するか」**が指標となり得る

## データの整合性を保つ

ドメイン駆動設計とは直接関係のない話、でも大事な話

AさんとBさんが同時にデータを更新すると、タイミングによっては意図しない動きになる
たとえば2人が同時にSNSアカウントをつくり、同じIDで登録できてしまったら、それは整合性がとれていない（とするSNSが大多数のはず）

### ユニークキーによる防衛

ユニークキー**だけに**頼るのは危険

- 今はユーザ名を重複確認のキーとしている
  - 将来、メールアドレスをもとに重複確認をするようルール変更があるかもしれない
    - ドメインが特定の技術基盤（データベース）に頼ると、ルールがドメインに書かれておらず、上記のような事態のとき（ルール変更時）に影響範囲特定に漏れが生じる

**ルールはコードに書く。ユニークキー制約はあくまで補助として使うべし**

### トランザクションによる防衛

こちらは一般的な方法らしい
データの書き換え作業がひととおり終わったあと、コミットにより変更を確定する

以下2つの方法が紹介されている

- [トランザクションスコープ](#トランザクションスコープ)
- [ユニットオブワーク](#ユニットオブワーク)

#### トランザクションスコープ

C#の機能ということで、[MS Docs - トランザクション スコープを使用した暗黙的なトランザクションの実装](https://docs.microsoft.com/ja-jp/dotnet/framework/data/transactions/implementing-an-implicit-transaction-using-transaction-scope)

使い方は、いたって簡単（使ったことないけど）

`using`で囲って最後に`Complete()`を呼べばいい

```cs
try
{
    using (TransactionScope scope = new ())
    {
        using (SqlConnection connection = new (connectString1))
        {
            connection.Open();
            // データベースとの通信処理
        }

        // コンプリートメソッドが呼ばれなければロールバックする
        scope.Complete();
    }
}
catch (TransactionAbortedException ex)
{
    // トランザクションが打ち切られた際の処理
}
```

#### ユニットオブワーク

ユニットオブワークは、任意のオブジェクトの**変更を記録する**オブジェクト

[サンプルコード](https://github.com/nrslib/itddd)では使われておらず（先述のトランザクションスコープを採用している）、本書の内容だけで理解するのが難しい
`dirty`や`clean`について記載が省略されているのが難しさの一因だと思う

[Qiita参考記事・・・ユニットオブワークを実践している](https://qiita.com/TakashiOnawa/items/19258d9a006240a48ec7)

##### ユニットオブワークのクラス図

- ドメインオブジェクトである`User`や`Circle`は`Entity`クラスを継承する
- `Entity`クラスは内部で`UnitOfWork`を使用する
- アプリケーションサービスは`UnitOfWork`のコミットをする（それだけ？）

```plantuml
class UnitOfWork
{
    + RegisterNew(object)
    + RegisterDirty(object)
    + RegisterClean(object)
    + RegisterDelete(object)
    + Commit()
}

abstract class Entity
{
    # MarkNew()
    # MarkDirty()
    # MarkClean()
    # MarkDelete()
}
note right: ユニットオブワーククラスのラッパー 

class User
{
    + User(UserName)
    + ChangeName(UserName)
}
note bottom: コンストラクタで MarkNewを呼ぶ\n名前変更メソッドで MarkDirty を呼ぶ

ApplicationService --> UnitOfWork  
UnitOfWork <.. Entity
Entity <|-- User
Entity <|-- Circle

```

##### Entity Framework もユニットオブワークを採用している（らしい）

```cs
class UserDbContext : DbContext
{
    public DbSet<User> User { get; set; }
}
```

`DbSet<TEntity>.Remove(TEntity)`でマークをして、`DbContext.SaveChanges`で変更をコミットするイメージだろうか

> 指定されたエンティティを Deleted としてマークすることにより、SaveChanges が呼び出されたときにデータベースから削除されるようにします。