SoftwareDesign ドメイン駆動設計入門をまとめてみた

# ドメイン駆動とは

## ドメイン駆動はなぜ大事？

- **事業活動の存続と発展のためには、事業の変化とともに成長と進化を続けるソフトウェアが必要である**

⇒企業が社会情勢に合わせて成長するためには、企業のシステムも合わせて変化、成長しないといけない。そのため、システムの変更や改善が容易にできるプログラムを構成する必要がある。

## ドメインとは？

- **ドメインとは、ソフトウェアが対象とする「領域」である。**

⇒ドメイン駆動設計では、上記の図のV字モデルの最上部の事業活動と最深部のソフトウェアの設計を直接的に強く関連付けることを目標とする。

- **問題解決の対象領域である**。

## ドメインモデルとは？

- モデルとは、「簡略化」である。

⇒膨大な情報の中から要点を抜き出してわかりやすく整理したモデル。

※ソフトウェアの対象領域（ドメイン）である事業活動の複雑さの要点を整理して簡略化したもの

- **ドメインモデルの３つの目的**
    1. **知識をかみ砕く**
    
    →業務のやり方や決め事を開発者が理解することが大切。重要じゃない情報がノイズにならないようにする。
    
    1. **継続的な学習と深い理解**
    
    →対象領域の知識を継続的に学ぶことが大切。業務を理解し、複雑な業務ルールを動くソフトウェアを実装するには、継続的にドメインモデルを成長させ、業務を深く理解し続ける必要がある。
    
    1. **用語や言い回しが異なる問題の解決**
    
    →同じ意味でも、企業や部門間でも言い回しが違う可能性がある。このような言葉のばらつきの問題を解決するための考え方がユビキタス言語。ユビキタス言語を使用して、同じ言葉を使って開発する。
    

### ユビキタス言語とは？

ソフトウェアの様々な活動を通じて、一貫して「同じ言葉を使ってソフトウェアを開発しよう」という考え。

当面の課題にとって重要な言葉を特定、選定していき、その言葉を全員が意識して同じ意味として使うようにすれば、その言葉が軸になり、その周りの言葉も意味や使い方が整合する、というのがドメイン駆動開発のユビキタス言語の考え方。

### ドメインモデルの3つの使い方

1. **業務知識の要点の整理**
    1. 業務プロセスに注目する❌
        
    2. 業務データに注目する❌
        
    3. 業務ルールに注目する⭕️
    業務ルールを表現するクラスを表現しながら、業務ルールに基づく計算判断ロジックの置き場としてメソッドを作っていくスタイル
        
        以下の点が他と違う
        
        - 管理番号の代わりに積載量・サイズという属性に注目している
        - 予約を航海と貨物の関係（線）として表現している
        - データモデルの予約の代わりに業務ルールであるオーバーブッキングクラスが明記されている
2. **関係者が意図を伝える時の基本語彙**
3. **クラス設計の基本構造**

### 業務ロジック（業務ルール）を記述する方法

- ドメインモデルを構成するクラスには業務ルールに基づく計算判断ロジックのみ記述
- **業務ルールの整理**
    - 価値の提供と対価
        - 例：輸送業の場合、提供するのは「輸送」であり、オーバーブッキングは輸送を約束するブッキング（予約）業務に関する業務ルールである。
    - 約束とリスク
        - 価値の提供と対価は時間軸から考え、不確定要素が多い。そのリスクを避けるために「約束」があり、その決め事が業務ルールである。
    - 売上と費用
        - 売上を大きくし費用を小さくするための決まりごとも重要な業務ルール である。
    - 市場での競争と事業の存続優位性
        - 業務が複雑であり、独自性が高い領域は事業が成長し発展していくための中核の業務領域であり、ドメイン駆動設計を活用すべき領域である。（コアドメイン※下記に詳細あり）

- **業務ルールを表現する基本部品**
    1. **値オブジェクト（Value Object）**：
    値オブジェクトは、単純なデータ構造を表現するためのオブジェクトです。これは、不変であることが一般的であり、同じ内容を持つ場合には等価であるとみなされます。例えば、日付、金額、住所などが値オブジェクトの例です。値オブジェクトは、業務ルールや概念の特定の側面を表現するのに使用されます。
        
        ```java
        public class Address {
            private String street;
            private String city;
            private String zipCode;
        
            public Address(String street, String city, String zipCode) {
                this.street = street;
                this.city = city;
                this.zipCode = zipCode;
            }
        
            // Getter methods
        }
        ```
        
    2. **コレクションオブジェクト（Collection Object）**：
    コレクションオブジェクトは、複数の要素をまとめて扱うためのオブジェクトです。これにはリスト、セット、マップなどが含まれます。例えば、注文に関連する商品のリスト、顧客の連絡先情報のセットなどがコレクションオブジェクトの例です。これにより、関連する要素をまとめて管理しやすくなります。
        
        ```java
        public class ShoppingCart {
            private List<String> items;
        
            public ShoppingCart() {
                items = new ArrayList<>();
            }
        
            public void addItem(String item) {
                items.add(item);
            }
        
            // Getter method for items
        }
        ```
        
    3. **集約（Aggregates）**：
    集約は、複数の関連するオブジェクトをまとめて扱うための概念です。集約は、一つのルートエンティティとそれに関連する値オブジェクトやエンティティを含むことがあります。**集約内では、一貫性を保つためのルールや制約が適用**されます。例えば、注文とその注文項目（商品と数量のペア）が集約の例です。
        
        ```java
        public class OrderItem {
            private String product;
            private int quantity;
        
            public OrderItem(String product, int quantity) {
                this.product = product;
                this.quantity = quantity;
            }
        
            // Getter methods
        }
        
        public class Order {
            private String orderId;
            private List<OrderItem> items;
        
            public Order(String orderId) {
                this.orderId = orderId;
                items = new ArrayList<>();
            }
        
            public void addItem(String product, int quantity) {
                OrderItem item = new OrderItem(product, quantity);
                items.add(item);
            }
        
            // Getter methods for orderId and items
        }
        ```
        
    4. **区分オブジェクト（Enumeration）**：
    区分オブジェクトは、一連の定数や列挙値を表現するためのオブジェクトです。例えば、商品の状態（有効、無効、販売中止など）や顧客のタイプ（一般、プレミアムなど）が区分オブジェクトの例です。これにより、特定の状態やタイプをコード内で明示的に管理できます。
        
        ```java
        public enum OrderStatus {
            PENDING,
            SHIPPED,
            DELIVERED
        }
        ```
        
    5. **モジュール（Modules）**：
    モジュールは、コードの論理的な部分を表現するための概念です。モジュールは、特定のドメインや機能に関連する一連のクラスやコンポーネントをまとめます。これにより、コードの構造が簡潔になり、変更が隔離されます。
        
        ```java
        // 注文関連のモジュール
        public class Order {
            // Order クラスの定義など
        }
        
        public class OrderService {
            // 注文の処理に関するクラス
        }
        
        // 商品関連のモジュール
        public class Product {
            // Product クラスの定義など
        }
        
        public class ProductService {
            // 商品の操作に関するクラス
        }
        ```
        

## ドメイン駆動設計とは？

### ドメイン駆動設計の基本の考え方

※モデルは知識の習得と整理の道具であり、意図を伝えるための認識合わせの道具。そして、最終的な成果物（動くプログラムのソース）に結びつける。

↓

「設計にそのまま使えるモデルを使って知識の整理や意図の伝達をやる」

「設計、知識の整理、意図の伝達の３つの用途に役立つモデルを見つけ、成長させていく」

### ドメイン駆動設計の関心ごと

※関心ごとは、「複雑な業務ロジック」です。

※画面、DB、通信ネットワークなどは主要な関心ごとではない。

- 複雑な業務ロジックを記述する部分をほかの構成要素から分割する。
    - アプリケーションを構成するほかの要素
        - 画面を使った表示や入力（画面）
        - データの記憶と参照（DB）
        - 通信を使った通知やデータ転送（通信ネットワーク）

⇒業務路軸（ドメインモデル）がアプリケーションの中核の構成要素であり、ほかの構成要素はドメイン駆動モデルの周辺にあり、ドメインモデルを利用する形にする。

- **ドメインモデル＋3層構造**

※「ドメインモデルのオブジェクトをそのまま使う」ことを重視

- **ドメインモデル＋ポート＆アダプター**

※様々な周辺の構成要素を「アダプター」として一般化し、アプリケーションのコアと周辺のアダプターをつなぐ仕組みを「ポート」として一般化したもの

- **クリーンアーキテクチャ**
    - 関心ごとを分割すること
    - 分割した構成要素の依存関係を単純にすること

1. **依存性逆転の原則（Dependency Inversion Principle）**
この原則は、高レベルのモジュールが低レベルのモジュールに依存すべきではなく、どちらのモジュールも抽象に依存すべきだと述べています。つまり、具体的な実装ではなく、インターフェースや抽象クラスに依存するべきです。これにより、変更が発生したときにも、上位層と下位層の間の依存性が緩和され、変更がより容易になります。
2. **クリーンアーキテクチャのレイヤー**
クリーンアーキテクチャは、内側から外側に向かっていくつかのレイヤーに分割されます。これらのレイヤーは、外側のレイヤーに依存せず、内側のレイヤーに関する詳細を隠蔽します。
    - **エンティティ（Entities）**：ビジネスロジックやデータ構造を表現する部分。内部でのみ存在し、他のレイヤーに依存しません。
    - **ユースケース（Use Cases）**：アプリケーションのユースケースやビジネスルールを定義します。エンティティを組み合わせて特定の動作を実現します。
    - **インターフェースアダプター（Interface Adapters）**：ユースケースを特定のフレームワークや外部ツールに適応させる役割を果たします。具体的には、プレゼンテーション層（UI）やデータベースとのやり取り、データの変換などを担当します。
    - **フレームワーク・ドライバー（Frameworks and Drivers）**：最外部の層で、アプリケーション全体のランタイムやフレームワークとの接続を担当します。f
3. **インターフェースの使用**
各レイヤーは、次の内側のレイヤーに対してインターフェースを定義します。たとえば、ユースケースは、エンティティのメソッドを使用するためのインターフェースを持ちます。これにより、内側のレイヤーの実装詳細を隠蔽し、外側のレイヤーは抽象化されたインターフェースにのみ依存します。

### 設計のやり方

1. **ソフトウェアの分解は機能ではなく型**
通常、ソフトウェアを設計する際、その機能や操作に基づいてコードを組織化することが考えられます。しかし、DDDの観点では、機能による分解よりも、ドメイン内での概念やエンティティに基づいてコードを組織化することが重要です。つまり、ドメイン内の要素（たとえば、顧客、注文、商品など）を重視し、それらの要素間の関係やビジネスルールを中心に設計を行います。
2. **型はクラス（オブジェクトの分類）に基づく**
「型」は、ドメイン内の概念やエンティティのことを指します。これは、顧客、注文、商品などの具体的な要素を指すものです。この要素をクラスとして表現し、そのクラスがその型の振る舞いや属性を表現するようにします。例えば、"Customer" クラスは、顧客に関する情報や操作をカプセル化したものです。
3. **クラスを唯一のモジュール（プログラミング単位）にする**
DDDでは、クラスを単位としてコードを組織化することを奨励しています。つまり、1つのクラスが特定の型や概念に関連する属性と操作を持ち、そのクラス内に関連するロジックを集約します。これにより、クラスは独立してテスト可能であり、変更があった際にも影響が限定されるため、保守性が向上します。

→簡単に言えば、DDDは、ドメイン内の重要な概念や要素をクラスとしてモデル化し、それぞれのクラスが独立して働くように設計するアプローチです。これにより、コードはドメインの複雑さに適合し、ビジネスの要求に柔軟に対応できるようになります。

## 分散アーキテクチャ

全体は、自律して活動する複数の構成要素が動的につながってネットワークを構成しているもの、というとらえ方をします。

このとらえ方では、固定的な構造はありません。構成要素（コンポーネント）は独自に進化できるし、コンポーネント間のつながり方も時間とともに変わっていく、というとらえ方です。

この変動性がシステム全体の柔軟性と発展性を生み出します。　

全体を俯瞰するときに、巨大な単一モデルとしてとらえるのではなく、独立性の高い構成要素の動的なネットワークとしてとらえるのがドメイン駆動設計の「戦略的な設計」の根底にある考え方。

### 分散モデル

- 境界づけられたコンテキスト
    - 1つのドメインモデルが対象とする範囲を限定するアプローチ
- コンテキストマップ
    - それぞれのコンテキストの中だけで意味を持つ複数のドメインモデルをどう繋げるかを検討するパターン
- コアドメイン
    - 全体の繋がりが複雑になり場合、全体の中核になる要素に焦点を合わせることで、全体の秩序を生み出すという考え方

→**実現方法(マイクロサービスアーキテクチャ)**

※機能の分割だけでなくデータベースも分割する

※連結部分を通信にし、個々のサービスの独立性を高める

## 開発プロセスに取り入れる

- 開発方法
    - **開発がイテレーティブ（反復的）である**
        - 範囲を限定し、コアに集中する
        - 思い込みで業務ルールを捉えていないか検証する
        - 業務に興味を持つ
        - モブプロをし、和気あいあいと業務理解に取り組むとで興味を促進
        - 業務に合わせてチーム分けする
        - メンバーの入れ替わりに対応するため、ソースコードに業務知識を記述する
        - 作業工程にドメインモデルを成長させるタスクを追加する
    - **開発者とでメインエキスパート（業務に詳しい人）が密接に関わっている**
        - 顧客に前もって説明しうまく巻き込む
        - 顧客ではないが業務に詳しい人を巻き込む
        - クラス名やパッケージ名を顧客と同じ言葉（日本語）で実装する（ユビキタス言語）

## ドメインモデルを使う側のパターン

- アプリケーションの中核を動かす3つのパターン
    1. **ユースケース (Use Case)**:
    ユースケースは、システムがユーザーや外部の要求にどのように応答するかを記述したシナリオやシナリオの集合です。ユースケースは特定の機能や機能群を表し、ユーザーの目線からシステムの振る舞いを理解しやすくします。例えば、「ユーザーが商品をカートに追加する」といったアクションがユースケースの一例です。
    2. **ファクトリー (Factory)**:
    ファクトリーは、新しいオブジェクトを生成するためのメソッドやクラスです。オブジェクトの生成には複雑なロジックや手続きが必要な場合、または生成されるオブジェクトが特定の状態や制約を持つ必要がある場合に使用されます。例えば、商品を作成するためのファクトリーは、適切な属性やルールに従った商品オブジェクトを生成します。
    3. **リポジトリ (Repository)**:
    リポジトリは、データベースや永続的なデータストレージとアプリケーションコードの間の仲介役です。リポジトリはデータの永続化や取得、検索を行い、アプリケーションコードに対してオブジェクトを提供します。ドメインオブジェクト（エンティティや値オブジェクト）の永続化と取得をリポジトリを通じて行います

## ドメイン駆動設計の用語集

### ドメイン

- 「事業活動の領域」「業務の領域」
    - 領域には「事業方針」や「業務ルール」がある

### モデル

- 「簡略化」
    - ドメインにおける「選択」された側面を記述し、ドメインに関連した問題を解決するのに使用できる「抽象体系」
    - ドメインモデルの意味：「ルールが適用される範囲限られた範囲」を「簡略化」したもの

### ユビキタス言語

- 開発に関わる関係者全員が「いつでもどこでも同じ言葉を使う」という目標を表現した言葉

### 境界づけられたコンテキスト

- 言葉が同じ意味で通用する範囲を特定するためのパターン

### モデル設計

- 設計の骨格を提供すること
    1. **エンティティ (Entity)**
    エンティティは、システム内で一意に識別される対象やオブジェクトを表すもの。
    2. **値オブジェクト (Value Object)**:
    値オブジェクトは、システム内で値そのものを表すオブジェクト。
    3. **集約 (Aggregate)**:
    集約は、関連するエンティティや値オブジェクトをグループ化して、一つのまとまりとして扱う概念。
    4. **ドメインイベント (Domain Event)**:
    ドメインイベントは、ドメイン内で起こる重要な事象や変更を表すオブジェクト。何が起こったかを記録するために使用され、異なる部分のコンテキスト間で情報を伝達するのに役立つ。
    5. **ドメインサービス (Domain Service)**:
    ドメインサービスは、特定のドメインロジックや処理を提供するサービス。エンティティや値オブジェクトだけでは適切に表現できない複雑な処理やルールをカプセル化します。

### まとめ

「明示的に**境界づけられたコンテキストの中**で、関係者全員が**同じ言葉（ユビキタス言語）**を使いながら、重要な業務知識を**ドメインモデルとして抽出**し、それを**設計の骨格として動く**ソフトウェアを作っていく」