【ドメイン駆動設計】ビジネスの理解を促進させるドメインモデルという考え方とそれを取り巻く概念について説明する


この記事は [クラウドワークス Advent Calendar](https://qiita.com/advent-calendar/2021/crowdworks) 2021 の 21日目の記事です。

## はじめに
こんにちは！
欧州サッカーと総合格闘技と酒とドメイン駆動設計が好きなエンジニアの[@d4te](https://twitter.com/d4te_kun)です。[crowdworks.jp](https://crowdworks.jp/)というプロダクトの施策チームでリーダーをやっています。
自分はドメイン駆動設計が好きで、その中でも解決すべき課題を明確にしソフトウェアの価値を高めるドメインモデルの考え方が好きです。

この記事ではドメインモデルの素晴らしさを伝えるべく、ドメインモデルとそれを取り巻く周辺の概念について説明していこうと思います！

※ ValueObjectやRepositoryなどドメイン駆動設計の設計パターンについてはこの記事では言及しません。

## そもそもソフトウェアを開発する目的って？
多くの場合は「ソフトウェアによって何らかの問題を解決するため」と言えると思います。そして問題を解決するためには問題や問題を取り巻く世界について学ぶ必要があります。

例えば会計システムを開発するためには会計業務を学ぶ必要がありますし、採用管理システムを開発する場合は採用業務を学ぶ必要があります。

そしてその学習で得られた問題解決に貢献する重要な知識をドメイン知識と呼びます。ドメイン駆動設計は最も重要であるドメイン知識を発見し、他の技術的関心事などから遠ざけることによって、ソフトウェアを変更容易で継続的に問題を解決し続けるものにするための設計思想です。

## ドメインとは何か
>ソフトウェアによって何らかの問題を解決するため

ソフトウェアを開発する目的を上記のように言いました。
ドメインとは**問題解決のためにソフトウェア化しようとする対象の領域**のことを指し、**問題領域**とも呼びます。

例えばECサイトであれば「問題」は下記のようなイメージです。

- 商品を購入したい
- 支払いを行いたい
- プロダクトの健全性を維持したい

そして、ドメインは問題をソフトウェアによって解決する領域として**解決領域**を持っています。

### 解決領域
**ドメインに存在する問題をソフトウェアによって解決する領域**（=境界づけられたコンテキスト）のことを解決領域と呼びます。
解決領域には問題を解決する手段であるユースケースと問題の解決に必要な概念であるドメインモデルが存在します。それぞれ後述します。

### わかりにくいので図で表現してみる
「インターネットでモノを販売したい」というドメインとその解決領域を下記のように表現してみました。

<div style="text-align: center">
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195724/e6f36937-7f9e-b8b1-4e07-647931a2b8ae.png" alt="問題領域と解決領域と内部に存在する概念の説明" />    
</div>

一番抽象度の高いレイヤーに「インターネットでモノを販売したい」というドメインが存在し、さらにそれぞれ複数の関心事単位でドメインが存在しているイメージです。それぞれのドメインの問題をソフトウェアによって解決する領域が存在し、その中にはユースケースとドメインモデルが存在します。

## ユースケース
ドメインに存在する問題を解決するための手段をユースケースと呼びます。
ユースケースは**ユーザーの要求に対して実行されるべきシステムの振る舞い**を意味しています。

また、ユースケースを実行できるのはアクターと呼ばれる概念です、アクターはシステム内でユーザーがなることができる役割を意味しています。
ECサイトであれば出品者 / 店 / 管理者などがいると考えます。


例えば下記などはユースケースに該当します。

- 顧客として商品をカートに入れる
- 顧客として商品を購入する
- 管理者として誹謗中傷コメント投稿者を凍結する

## ドメインモデル
ドメインに存在する問題を解決するためのモデルをドメインモデルと呼びます。
ドメインモデルはドメインに存在する物事の特定の側面を**抽象化**[^1]したものです。

[^1]: 抽象化とは、必要な要素のみを取り出し他は捨てること



※ ActiveRecordなどDBアクセス効率化の文脈で語られるモデルとは別概念です。

### 定義だけだと理解しづらいのでサッカーのユニフォームを例に説明してみる
自分はサッカーが好きなので、サッカーのユニフォームを使ってドメインモデルを説明してみようと思います。

<div style="text-align: center">
    <img style="width: 400px; display: inline-block;" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195724/43ddf0f6-b44c-1132-1674-08aab2b22003.jpeg" alt="ユニフォームの画像" />    
</div>

この画像は自分が購入したサッカーのユニフォームで、一見選手が着ているユニフォームと同じように見えますが、実は結構違います。

ユニフォームを初めて購入しようとした時に店員さんに違いを聞いてみたところ下記のように違うことがわかりました。

```plantuml
object 実際に選手が来ているユニフォーム {
    通気性が良い
    伸縮性抜群
    軽い
}
```

```plantuml
object 筆者が持っているユニフォーム {
    長持ちするように作られている
    ※ 通気性や伸縮性が優れているわけではない
}
```

さらに「なぜ実際に選手が着ているものと性能が違うんですか？」と質問したところ、店員さんは
「ファンが購入するユニフォームは**観戦を目的として作られており、ファンとして長い間ユニフォームを着用できるよう耐久性を重要視**しています、逆に**サッカーをプレーする目的では作られていないため、伸縮性や通気性などの性能はありません。**」と教えてくれました。また、選手が着用しているユニフォームを「オーセンティックユニフォーム」、ファンが観戦の時に着用するユニフォームは「レプリカユニフォーム」と呼ばれているとも教えてくれました。

つまり選手が着用している「オーセンティックユニフォーム」では**通気性**や**伸縮性**という要素を必要としていますが、**耐久性**という要素は捨てています。
一方で自分が所有している「レプリカユニフォーム」は**耐久性**という要素を必要としていて、**通気性**や**伸縮性**などの要素は捨てている、と言えます。

そしてこれらを**ドメインに存在する問題を解決するためのモデル**という定義から考えると、下記のように言えます。

- オーセンティックユニフォーム（実際に選手が着用している）
    - **快適にプレーしたいというドメインの問題解決**に貢献
- レプリカユニフォーム（ファンが観戦などの時に着用している）
    - **ファンとして長くサッカー選手を応援したいというドメインの問題解決**に貢献

それぞれ異なるドメインの問題解決を関心としたドメインモデルと言えます。

このようなドメインの問題を解決するための**要素の取捨選択**（抽象化）するプロセスをドメインモデリングと呼び、得られたモデルをドメインモデルと呼びます。
そして問題解決に貢献する有能なモデルを得るためには**ドメインエキスパート**と呼ばれる、ドメインに詳しい人と共にドメインを学習し、ドメインモデリングしていくことが重要です。

## ドメインモデルと境界づけられたコンテキスト
境界づけられたコンテキストは解決領域のことを指していて、ドメインモデルを同じ意味で利用できる範囲を定義するものです。
境界づけられたコンテキストによって、ドメインモデルの精度を向上させることができます。

サッカーのユニフォームを例に説明しようと思います。
境界づけられたコンテキストを意識せず、愚直にユニフォームとして考えれば下記のようなドメインモデルになります。

```plantuml
namespace コンテキストを意識していない {
    class ユニフォーム {
        伸縮性に優れているか否か
        通気性に優れているか否か
        軽いか否か
        耐久性があるか否か
    }   
}
```

簡単な例なので問題はないように見えますが、これではチームメンバーとのコミュニケーションで「オーセンティックユニフォーム」のことを話しているのか「レプリカユニフォーム」について話しているのか混乱しやすくなり、かつこのように概念同士を一緒くたにするとFatになっていきます。


境界づけられたコンテキストを意識すれば本来分けるべき概念が分離され、コミュニケーション齟齬などを減らすことができ、かつ複数の意味を持ったFatなモデル等を防ぐことができます。

```plantuml

namespace サッカーの選手として快適にプレーしたいコンテキスト {
    class ユニフォーム {
        伸縮性が良い
        通気性が良い
        軽くてプレーしやすい
    }
}

namespace ファンとして長くサッカー選手を応援したいコンテキスト {
    class ユニフォーム {
        耐久性がある
    }
}
```

## 実際の機能開発でどのようにドメインモデルを発見するか
ここでは自分が概念整理のために行うドメインモデリングのやり方について書いてみようと思います。

実際にヨーロッパの某プロサッカーチームが運営しているチケット販売サイトの1機能を例にドメインモデリング をしてみようと思います。

※ ホームの試合はホームチームがチケットを販売するという文化になっているため、各サッカーチームが独自のチケット販売サイトを持っている[^2]

[^2]: プロサッカーチームはリーグに所属しており、リーグには複数のチームが所属している。各チームはスタジアム（サッカー場）を所有しており、ホームと呼称する。

ドメインモデリングの手順は下記になります。

- モデリング対象のユースケースの詳細を書き出す
- 要件からドメインモデルを抽出する
- ドメインモデル図を作成する
- 発見したドメインモデルをソフトウェアに落とし込む


### モデリング対象のユースケースの詳細を書き出す
今回は「試合のチケットを選択する」というユースケースをモデリングしてみようと思います。
ユースケースの仕様は下記になります。

-  「チケットを選択する」の意味
    - アクター: サイト内会員
    - 観戦したい試合を選択する
    - 試合選択後、観戦場所となる座席を選択する
    - チケット購入に必要な金額が閲覧できる

- 要件
    - 全ての座席には基本料金が設定されている
    - 座席には観戦する場所（エリア）ごとに料金が設定されている
    - 座席の料金は基本料金とエリアごとの料金の合計で決定する
    - 座席と試合の料金の合計が請求される金額となる
    - 会員には一般会員とVIP会員が存在している
        - VIP会員は試合開催予定日の2週間前に試合を選択可能
        - 一般会員は試合開催予定日の1週間前に試合を選択可能となる

※ 今回は購入（支払い）は含めません

このタイミングでユースケースの実行者であるアクターが誰なのかも明らかにしておいた方が良いです。
今回の例では会員登録を済ませた人がアクターですが、サッカーチームのチケットは旅行代理店なども代行販売していることがあり、そのために「旅行業者としてサッカーチームからチケットを選択/購入する」している可能性があります。その場合、一般会員が解決したい課題と旅行関係業者が解決したい課題が異なる可能性も高く、モデリングの精度も曖昧になりやすいためです。


### 要件からドメインモデルを抽出する
自分は要件に書かれた名詞がドメインモデルになる可能性が高いと考えます。
なので次はユースケースの要件に存在する名詞を抜き出してみます

- 会員
- マッチ（試合）
    - サッカーでは試合のことをマッチと呼ぶことが多い気がしたのでマッチにしてみる
- 座席
- 基本料金
- チケット
- 座席エリア
- エリア料金
- 請求金額
- 試合開催予定日

### ドメインモデル図を作成する
そしてクラス図のようにドメインモデルを関連づけ、モデルに関連する知識やルールも書き出していきます。

<div style="text-align: center;">
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195724/8e5c641f-b601-0701-b1e5-94f50d0b8946.png" />    
</div>

### 発見したドメインモデルをソフトウェアに落とし込む
このプロセスをドメインエキスパートや他のチームメンバーと議論しながら行うことで、開発に関わるメンバー全員でドメインに詳しくなり、同時に開発メンバーとの認識を合わせることが可能です、それに加えてドメイン駆動設計は発見したドメインモデルをソフトウェアとして表現するためにEntityやValueObjectなど強力な設計パターンも兼ね備えています。これによって発見したモデルを高品質でソフトウェア化することが可能となります。


## まとめ
- 問題解決のために**ソフトウェア化しようとする対象の領域**をドメイン（問題領域）と呼ぶ
- **ドメインに存在する問題をソフトウェアによって解決する領域**を解決領域と呼ぶ
- ユースケースは**ユーザーの要求に対して実行されるべきシステムの振る舞い**を意味する
- ドメインモデルは**ドメインに存在する物事の特定の側面を抽象化したもの**
- 境界づけられたコンテキストは**ドメインモデルを同じ意味で利用できる範囲**のこと
- ドメインモデルはユースケースの仕様などから発見できる
- ドメインモデル同士の関連 / 知識 / ルールをドメインモデル図に記入するとドメインが理解しやすくなる
- ドメインモデリングによって**チーム全員でドメインに詳しくなる**

## おわりに
ドメインモデルとそれを取り巻くドメインやユースケースなどの概念を説明し、あわせて自分が日頃行っているドメインモデリングについても書いてみました。

この記事がソフトウェアの価値を高めるための一助になれば嬉しいです！