# ドメイン駆動設計 入門 輪読会 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}]"}
    523 views