# ドメイン駆動設計 入門 輪読会 vol.11
2022/02/10 [@kdnakt](https://twitter.com/kdnakt)
---
### 今日の範囲
- 11章 アプリケーションを1から組み立てる
- 11.1 アプリケーションを組み立てるフロー
- 11.2 題材とする機能
- 11.3 サークルの知識やルールを
オブジェクトとして準備する
- 11.4 ユースケースを組み立てる
- 11.5 まとめ
---
### 今日の概要
- 登場したパターンを実際に利用してみる
- 機能洗い出し
- ドメインオブジェクト定義
- ユースケース実装
---
### アプリケーションを組み立てるフロー
- 新たな機能を追加してみる
1. どういった機能が求められているか?
1. 必要なユースケースは?
1. 概念、ルールから
ドメインオブジェクトを準備
1. ドメインオブジェクトを使い
アプリケーションサービスを実装
---
### 題材とする機能
- これまで:SNSのユーザ登録機能
- 今回:SNSのサークル機能
- 同じ趣味を持つユーザの交流を促す
- 必要なユースケース
- サークルの作成
- サークルへの参加
- サークルからの脱退
- サークルの削除
---
### サークルの前提条件
- ドメインのルールは以下のとおり
- サークル名
- 3文字以上20文字以下
- 全てのサークルで重複しない
- 参加者
- オーナーを含めて30名まで
----
#### ドメインオブジェクトの準備
- サークル
- 作成・削除のライフサイクルがある
=エンティティ
- Circle
- 識別子、名前などの値が必要
=値オブジェクト
- CircleId
- CircleName
----
#### ドメイン知識を表現する(1)
- 値オブジェクト
- サークル名:3-20文字
```java
public CircleName(String value) {
if (value == null) {
throw new RuntimeException("null value");
}
int len = value.length();
if (len < 3) {
throw new RuntimeException("too short");
}
if (20 < len) {
throw new RuntimeException("too long");
}
this.value = value;
}
```
----
#### ドメイン知識を表現する(2)
- エンティティ
```java
public Circle(
CircleId id,
CircleName name,
User owner,
List<User> members
) {
// 引数それぞれのnullチェック(省略)
this.id = id;
// 以下略
}
```
----
#### リポジトリ、ファクトリ
```java
public interface ICircleRepository {
void save(Circle circle);
Circle find(CircleId id);
Circle find(CircleName name);
}
```
```java
public interface ICircleFactory {
Circle create(CircleName name, User owner);
}
```
----
#### ドメインサービス
- サークル名の重複確認の振る舞い
```java
// CircleService.java
public boolean exists(Circle circle) {
Circle duplicated
= this.repo.find(circle.getName());
return duplicated != null;
}
```
---
### ユースケースの組み立て(1)
- サークルを作成するコマンドオブジェクト
```java
public class CircleCreateCommand {
// コンストラクタ、ゲッター省略
String userId; // オーナー
String name; // サークル名
}
```
:::info
変数名`ownerUserId`とかの方が良いかも?
:::
----
#### アプリケーションサービス(1)
```java
public class CircleApplicationService {
// コンストラクタなど省略
@Transactional
public void create(CircleCreateCommand cmd) {
// ユーザーIDからオーナー存在チェック
// ファクトリからサークルを生成
// サークル名の重複チェック
// サークルの保存処理
}
}
```
---
### ユースケースの組み立て(2)
- サークルに参加するコマンドオブジェクト
```java
public class CircleJoinCommand {
// コンストラクタ、ゲッター省略
String userId; // 参加者
String circleId; // サークルID
}
```
:::info
変数名`memberUserId`とかの方が良いかも?
:::
----
#### アプリケーションサービス(2)
```java
public class CircleApplicationService {
// コンストラクタなど省略
@Transactional
public void join(CircleJoinCommand cmd) {
// ユーザーIDからメンバー存在チェック
// サークルの取得
// サークルの人数制限チェック
// サークルへのメンバー追加
// サークルの保存処理
}
}
```
---
### ドメインルールの流出(1)
- サークルの人数制限チェックの実装
```java
// CircleApplicationService.java
// オーナー含めて30人の人数上限のチェック
if (29 < circle.getMembers().size()) {
throw new CircleFullException();
}
```
- コメントとコードで表現にズレ
- Circleクラス:オーナーとメンバーが別管理
- Circleクラスの内部実装が漏れている
- 重複記述は変更時に問題
- ドメインオブジェクトに実装すべき
----
#### ドメインルールの流出(2)
- メンバーの勧誘を行う処理
- 人数制限チェックの重複が発生
```java
// CircleApplicationService.java
@Transactional
public void invite(CircleInviteCommand cmd) {
// 招待元ユーザーの存在チェック
// 招待先ユーザーの存在チェック
// 招待サークルの存在チェック
// サークルの人数制限チェック <- 😰
// サークル招待オブジェクトの保存処理
}
```
----
#### ドメインルールの流出(3)
- メンバー数上限が変更になると...
- membersプロパティの利用箇所調査
- 29,30でコードを検索
- 他の概念の29, 30かもしれない
- ルールがプロダクト内に点在すると
修正箇所が散らばる
- 修正漏れがバグを引き起こす
- ルールのコードがサービスに
書かれたのが原因
- 集約によって解決できる!(次章)
---
### 今日の概要
- 登場したパターンを実際に利用してみる
- 機能洗い出し
- ドメインオブジェクト定義
- ユースケース実装
- 新たな課題:ロジックの点在(次章へ続く)
{"metaMigratedAt":"2023-06-16T19:05:05.319Z","metaMigratedFrom":"YAML","title":"ドメイン駆動設計 入門 輪読会 vol.11","breaks":false,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"df36d0f0-b67e-41ac-96b3-f3988326d230\",\"add\":4186,\"del\":360}]"}