コンテンツにスキップ

ドメイン駆動設計@アーキテクチャ

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. ドメイン駆動設計とは

オブジェクト指向分析設計から派生した分析設計の手法の一種。

オブジェクト指向分析設計のベタープラクティスを集め、より強化するために提唱された。

特に機能要件の多いアプリケーションに有効である。

必ずしもオブジェクト指向設計でドメイン駆動設計に沿う必要はなく、通常のオブジェクト指向分析設計を行っても問題はない。


02. ドメイン駆動設計の手順例

戦略的設計の手順例

ddd_strategic_design_flow

戦略的設計では、ドメイン全体から境界づけられたコンテキストを明確にする。

(1)

ドメインエキスパートと話し合い、ドメイン全体の中からコアドメインとサブドメインを切り分ける。

(2)

ドメインエキスパートの部署や業務フローの立ち位置によっては、同じ『名詞』や『動詞』であっても、意味合い/定義づけが異なる場合がある。この時、それぞれを異なる名前からなるユビキタス言語として定義づける。

(3)

ユビキタス言語を元に、境界づけられたコンテキストを定義づける。

(4)

コンテキストマップを作成し、境界づけられたコンテキスト間の関係を明らかにする。


戦術的設計の手順例

戦術的設計では、境界づけられたコンテキストをアーキテクチャやデザインパターンに落とし込む。

ここでは、SUDOモデリングを採用するものとする。

(1)

ドメインエキスパートと話し合いにビジネスルールや振る舞いをヒアリングする。

(2)

オブジェクトの具体化のためにユースケース図やオブジェクト図でオブジェクトを作成し、抽象化のためにドメインモデル図も作成する。

(3)

必要であればドメインエキスパートに再ヒアリングを行い、特にオブジェクト図とドメインモデル図を改善する。

(4)

ドメインモデル図を元に、クラス図を作成する。この時、モデルをエンティティや値オブジェクトを切り分けるようにする。

(5)

アーキテクチャ (レイヤード型、ヘキサゴナル型、オニオン型、クリーンアーキテクチャ) を決め、クラス図を元にドメイン層を実装する。

(6)

運用後に問題が発生した場合、特にオブジェクト図とドメインモデル図を修正する。場合によっては、デザインパターンに切り分ける。


03. 戦略的設計

ドメイン

▼ ドメインとは

ビジネスモデル全体で見た時に、ソフトウェア化の対象となる業務領域のこと。

▼ ドメインの分割方法

業務領域を分割する時の粒度は小さいほど良い。

業務領域を細分化する上で境目を見分ける方法として、会社の組織図から、部署ごとに担当する業務の種類を確認すると良い。

また、ビジネスモデルの一連の業務フローの中で、業務の担当者の属性が変化するタイミングに着目すると良い。

担当者が変われば、コールセンター部 (電話対応業務、顧客管理業務) 、法人営業部 (受注業務) 、マーケティング部 (ユーザー集客業務、広告運用業務) 、管理部 (会計業務、財務業務、採用業務) 、というように業務をより小さな単位で抽出できるはずである。

この時の注意点として、業務領域は、ToBやToCに関わらないことである。

例えば、ToCなドメインとしては、マーケーティング部による業務 (ユーザー集客業務、広告運用業務) がある。

*例*

インターネット広告代理店の例。

ビジネスモデルに基づく複数のドメインを以下に示す。

業務フローの担当者の変化として、まず問い合わせで注文を受けて広告アカウントを作成する『営業担当者』、制作した広告をアカウントに入稿する『制作担当者』、入稿された広告を運用して広告効果を高める『マーケティング担当者』、最後に広告の依頼者に料金を請求する『経理担当者』が挙げられる。

これにより、インターネット広告代理店のビジネスモデルは、各担当者に対応するドメインに分割できる。

internet_advertising_agency_domain

*例*

hacogym_business_model

完全個室ジムを運営するハコジムの例。

ビジネスモデルに基づく複数のドメインを以下に示す。

業務フローの担当者の変化として、まず個室ジムに適する物件を探す『物件担当者』、ジムのトレーナーを採用して会員に紹介する『採用担当者』、個室ジムの利用会員を獲得する『営業担当者』が挙げられる。

これにより、ハコジムのビジネスモデルは、各担当者に対応するドメインに分割できる。

hacogym_domain


コアドメイン、サブドメイン、ドメインエキスパート

▼ コアドメイン、サブドメイン、ドメインエキスパートとは

各ドメインのドメインエキスパートと、エンジニアが話し合いながら、ドメイン内の主要業務をコアドメイン、補助的な業務をサブドメインに分類する。

コアドメインやサブドメインに相当する業務に詳しい人 (アプリケーションの利用者本人、利用者の関心に詳しい人) がドメインエキスパートになることが多い。

core-domain_sub-domain_bounded-context

▼ サブドメインの委譲

コアドメインのソフトウェアは内製である必要があるが、サブドメインのソフトウェアは外製/内製のいずれでも問題ない。

必要であれば、他の業務系パッケージや業務系SaaS (例:決済代行のベリトランス、経理SaaSのマネーフォワード、総務人事SaaSのサイボウズなど) にドメインロジックを委譲し、これのAPIをコールしてデータを取得するような設計でもよい。

ただ、コアドメインが外部に依存すると、そのSaaSで障害が起こった時にコアドメインのシステムも停止する可能性があるため、注意が必要である。

▼ 記法

*例*

完全個室ジムを運営するハコジムの例。

ドメインのうちで、個室ジムドメインに基づくコアドメインとサブドメインを以下に示す。

コアドメインは予約ドメイン、それ以外はサブドメインとしている。

hacogym_subdomain

*例*

ECサイトを運営するアスクルの例。

ドメインのうちで、個人向け販売ドメイン (サイト名はLOHACO) に基づくサブドメインを以下に示す。

配送/注文/商品/ユーザ管理/在庫/受注をそれぞれサブドメインとしている (コアドメインは明言されていない) 。


ユビキタス言語

▼ ユビキタス言語とは

同じ境界づけられたコンテキスト内で1個の意味合い/定義付けを持った『名詞』や『動詞』のこと。

ドメインエキスパート間で、特定の『名詞』や『動詞』の意味合い/定義づけが異なる場合、これを異なる名前からなるユビキタス言語として定義づける。

domain-model

▼ 抽出例

単語 意味合い 対応するドメインモデル
... ... ...
... ... ...


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

▼ 境界づけられたコンテキストとは

ドメインエキスパートの業務フローの立ち位置が異なれば、同じ『名詞』や『動詞』であっても、異なる意味合い/定義づけのユビキタス言語が使用される。

異なるユビキタス言語を元にして、境界づけられたコンテキストを設定する。

この時、ユビキタス言語は、他の境界づけられたコンテキストでは通じないものであればあるほど良い。

境界づけられたコンテキストそれぞれのユビキタス言語に合わせて、異なる名前でモデリングしていく。

境界づけられたコンテキストを定義しない場合、異なるユビキタス言語をコアドメインやサブドメイン間で共有することとなり、それぞれの関心に無関係なデータを保持することになってしまう。

▼ 記法

*例*

本を販売するECサイトの例。

コアドメインとサブドメインに基づいたユビキタス言語と境界づけられたコンテキストを以下に示す。

バイヤー (仕入れ) 部、マーケティング部、在庫管理部のドメインエキスパートは、『本 (商品) 』という名詞に対する意味合い/定義づけが異なる。

そのため、それぞれを『本』『クーポン』『在庫』というユビキタス言語として定義でき、モデル名/データ名はそれぞれのユビキタス言語に合わせた名前になる。

例えば、マーケの境界づけられたコンテキストでは、モデル名はCouponとなり、割引期間データを保持する必要があるが、仕入部や在庫部ではこのデータは不要である。

一方、ISBNは全ての境界づけられたコンテキストのモデルに必要なデータである。

境界づけられたコンテキストを定義しない場合、1個の商品モデルが全てのデータを保持することとなり、それぞれのドメインエキスパートが関心を持たないデータも保持することになってしまう。

book_ec_ubiquitous_language

*例*

完全個室ジムを運営するハコジムの例。

個室ジムドメインのコアドメインとサブドメインに基づく境界づけられたコンテキスト。

認証コンテキスト、予約コンテキスト、顧客管理コンテキスト、銀行支払いコンテキスト、クレジットカード支払いコンテキストがある。

hacogym_bounded-context

*例*

契約請求管理アプリケーションを提供するアルプの例。

コアドメインとサブドメインに基づいたユビキタス言語と境界づけられたコンテキストを以下に示す。

契約管理コンテキスト、商品管理コンテキスト、請求管理コンテキスト、がある。

アルプでは、ユビキタス言語を定期的に認識合わせするために、週次でユビキタス言語を追加し、また定義を更新している。

contract_billing_management_ubiquitous_language

*例*

会計アプリケーションを提供するfreeeの例。

財務会計コンテキスト、会計ワークフローコンテキスト、従業員管理コンテキスト、がある。

freee_bounded-context

*例*

アプリケーションがモデリングしている業務フローを考えるとわかりやすい。

ビジネス全体のどの部分をアプリケーションにしているのかによるが、例えばAmazonで以下の登場人物がいるとする。

  1. 受注管理する人は、商品ドメインモデルが持つ注文内容/値段に関するデータに関心がある。
  2. 在庫管理する人は、商品ドメインモデルが持つ在庫数に関するデータに関心がある。
  3. 発送管理する人は、商品ドメインモデルが持つ発送状況に関するデータに関心がある。
  4. 請求管理する人は、商品ドメインモデルが持つ請求状況に関するデータに関心がある。
  5. 仕入れ管理する人は、商品ドメインモデルが持つ仕入れ状況に関するデータに関心がある。

商品 (Item) というオブジェクトがあった時に、業務フローの中で商品を扱う人の文脈 (コンテキスト) によって、商品ドメインモデルの持つデータへの関心が変わる。

ここでいうデータとは、具体的に商品ドメインモデルを実装した時にプロパティとしてオブジェクトに持たせているデータである。

上記の1-4は、同じ商品ドメインモデルに対して関心の内容が異なるので、境界づけられたコンテキストが異なるとみなした方が良い。

別々のマイクロサービスとして分割し、それぞれのマイクロサービス上で異なる商品ドメインモデルを定義し、異なるデータを持たせることになる。

反対にモノリスだと、一つの商品ドメインモデルにコンテキスト関係なく全ての種類のデータを持たせて、全てのコンテキストで使い回す。


ドメインモデルの種類

▼ 隠蔽モデル

特定の境界づけられたコンテキストのみで使用するドメインモデルのこと。

bounded-context_domain-model_type

▼ 共有モデル

異なる境界づけられたコンテキスト間で、一部のデータを共有ドメインモデルのこと。

図では、在庫品目モデルがそれに相当する。

bounded-context_domain-model_type


コンテキストマップ

▼ コンテキストマップとは

広義のドメイン全体の俯瞰する図のこと。

コアドメイン、サブドメイン、境界づけられたコンテキストを定義した後、これらの関係性を視覚化する。

異なるサブドメインの間で異なるユビキタス言語を使用する場合、境界づけられたコンテキストはサブドメインをまたがない。

一方で、同じユビキタス言語を使用する場合、境界づけられたコンテキストは複数のサブドメインにまたがる。

できる限り、各境界づけられたコンテキストでは異なるユビキタス言語を使用して、境界づけられたコンテキストが複数のサブドメインにまたがないようにした方が良い (これ重要) 。

context-map

▼ 記法

*例*

完全個室ジムを運営するハコジムの例。

個室ジムドメインのそれぞれの境界づけられたコンテキストに基づくモデリング。

コアドメインの予約コンテキストとスマートロックコンテキストは、1個のマイクロサービスとして内製化している。

一方で、それ以外の境界づけられたコンテキストは外製化している。

hacogym_subdomain_modeling


04. 戦術的設計

MVCからレイヤードアーキテクチャへの変遷

▼ MVCと問題点

ドメイン駆動設計が提唱される以前、MVCの考え方が主流であった。

しかし、特にModelの役割が抽象的過ぎたため、開発規模が大きくなるにつれて、Modelに役割を集中させ過ぎてしまうことがあった。

MVCモデル

▼ MVCからレイヤードアーキテクチャへの移行

ドメイン駆動設計が登場したことによって、MVCは発展し、M・V・Cそれぞれの役割がより具体的で精密になった。

Modelの肥大化は、Modelが持つビジネスロジックをドメイン層、またCRUD処理をインフラストラクチャ層として分割することによって、対処された。

ドメイン駆動設計


DDDアーキテクチャの種類

▼ レイヤードアーキテクチャ

最初に提唱された実現方法。

レイヤードアーキテクチャのインフラストラクチャ層に対して、依存性逆転を組み込むかどうはには言及していない。

  • UI (コントローラー、認証認可)
  • アプリケーション層 (ユースケース)
  • ドメイン層 (ドメインモデル)
  • インフラストラクチャ層 (永続化)

layered-architecture

▼ レイヤードアーキテクチャに依存性逆転を組み込む

レイヤードアーキテクチャのインフラストラクチャ層に対して、依存性逆転を組み込んだもの。

ドメイン層のオブジェクトは、ドメイン層の他のオブジェクトに依存する以外、何のオブジェクトにも外部パッケージにも依存しない。

逆に考えれば、これらに依存するものはドメイン層に配置する必要はないと判断できる。

▼ ヘキサゴナルアーキテクチャ

別名『ポートアンドアダプターアーキテクチャ』という。

レイヤードアーキテクチャ

本質的には、他の『オニオンアーキテクチャ』『クリーンアーキテクチャ』に同じである。

▼ オニオンアーキテクチャ

本質的には、他の『ヘキサゴナルアーキテクチャ』『クリーンアーキテクチャ』に同じである。

onion-architecture

▼ クリーンアーキテクチャ

本質的には、他の『ヘキサゴナルアーキテクチャ』『オニオンアーキテクチャ』に同じである。

clean-architecture


ドメインモデル

▼ ドメインモデルとは

現実の業務を抽象化した概念のこと。

▼ ドメインモデル図

ドメインモデルの図のこと。

基本的にオブジェクト指向分析設計では、分析のためにユースケース図やオブジェクト図を作成し、その後に設計のためにクラス図やシーケンス図を作成する。

一方でドメイン駆動設計の分析では、オブジェクトの具体化のためにユースケース図やオブジェクト図でオブジェクトを作成し、抽象化のためにドメインモデル図も作成する。

この『具体例から抽象を導く』という作業により、オブジェクト思考分析よりも現実に沿ったモデリングが可能になる。

▼ ドメインオブジェクト図

ドメインモデルを具体的に実装した図のこと。

▼ コアドメイン/サブドメインのモデリング

ドメインモデル図を基に、コアドメイン/サブドメインのモデリングを実行する。

core-domain_sub-domain_bounded-context_modeling

▼ 記法

クラス図と同様にして、オブジェクト図のインスタンス間の関係性を参考にして、ドメインモデル間の関係性を設定する。

オブジェクト間がIDのみを使用する関係であれば『点線矢印』とし、加えてオブジェクト全体を使用する関係であれば『白塗りの菱形』とする (記号の表記方法は個人差がある) 。

この時、点線矢印の関係性であれば異なる集約に所属し、加えて白塗りの菱形であれば同じ集約に所属しているため、これらから集約の境界を設定する。

インスタンス間のリンク記号数を参考にして、多重度を付記する。

ビジネスルールを吹き出しに書き込むことにより、ソフトウェアの構造のみでなくビジネスルールも表す。

*例*

  1. 業務内容をヒアリングし、ユースケース図を作成する
  2. ヒアリング内容からユビキタス言語を抽出し、ドメインモデル図を作成する
  3. ドメインモデル図からエンティティと値オブジェクトを含むルートエンティティを作成する

*例*

とある映画チケット料金を題材に、ハッシュタグチケット料金モデリングとして、色々な方がユースケース図とドメインモデル図を作成されている。

いずれの方も非常に参考になる (モデリングは難しい) 。

(1)

映画チケット購入者の受注管理システムを開発する例を考える。

(2)

ドメインエキスパートへの要件定義が終えた想定で、ユースケース図を作成する。オブジェクト図は省略する。

ticket-modeling_little-hands_usecase-diagram_example

(3)

暫定的なドメインモデル図を作成する。little_hands_sさんは、IDのみを使用する関係であれば『実線矢印』とし、加えてオブジェクト全体を使用する関係であれば『黒塗りの菱形』としている。ユースケースから以下のオブジェクトを抽出する。この時、実装パターンをおおまかに予想しておく。また、矢印と菱形の関係性から、集約の境界を設定する。

ユースケース 抽出されたオブジェクト例 実装パターン 抽出された集約
映画、上映日時、枚数を選択する 上映時間帯オブジェクト、上映オブジェクト、映画オブジェクト エンティティ、値オブジェクト 上映集約、映画集約

ticket-modeling_little-hands_domain-model-diagram_example-1

(3)

ドメインモデル図を更新する。

ユースケース 抽出されたオブジェクト例 実装パターン 抽出された集約
映画、上映日時、枚数を選択する 上映時間帯オブジェクト、上映オブジェクト、映画オブジェクト エンティティ、値オブジェクト 上映集約、映画集約
同上 予約オブジェクト エンティティ 予約集約
割引を選択する 適用割引オブジェクト 値オブジェクト 予約集約
支払い金額を確認する 支払料金オブジェクト 値オブジェクト 支払い料金集約

ticket-modeling_little-hands_domain-model-diagram_example-2

(4)

ドメインモデル図を更新する。

ユースケース 抽出されたオブジェクト例 実装パターン 抽出された集約
映画、上映日時、枚数を選択する 上映時間帯オブジェクト、上映オブジェクト、映画オブジェクト エンティティ、値オブジェクト 上映集約、映画集約
同上 予約オブジェクト エンティティ 予約集約
割引を選択する 適用割引オブジェクト 値オブジェクト 予約集約
支払い金額を確認する 支払料金オブジェクト 値オブジェクト 支払い料金集約
同上 料金区分オブジェクト 区分オブジェクト (Enum) 料金設定集約

ticket-modeling_little-hands_domain-model-diagram_example-3

実装フェーズに入ってからの話になるが、料金区分オブジェクトはenum型として実装することになり、以下の様になる。

ticket-modeling_little-hands_enum_example

(5)

ドメインモデル図を更新する。

ユースケース 抽出されたオブジェクト例 実装パターン 抽出された集約
映画、上映日時、枚数を選択する 上映時間帯オブジェクト、上映オブジェクト、映画オブジェクト エンティティ、値オブジェクト 上映集約、映画集約
同上 予約オブジェクト エンティティ 予約集約
割引を選択する 適用割引オブジェクト 値オブジェクト 予約集約
支払い金額を確認する 支払料金オブジェクト 値オブジェクト 支払い料金集約
同上 料金区分オブジェクト 区分オブジェクト (Enum) 料金設定集約
同上 料金区分計算オブジェクト 値オブジェクト 料金区分計算集約

ticket-modeling_little-hands_domain-model-diagram_example-4

(6)

集約間の関係性のみに着目する、オブジェクト間の関係性は複雑であるが、集約間は単純であることが分かる。

ticket-modeling_little-hands_domain-model-diagram_example-4


04-02. ドメインモデリング手法の種類

ステートソーシングパターン

▼ ステートソーシングパターンとは

ドメインモデルの最新の状態を永続化する。

過去の状態は上書きしてしまう。

通信方式がリクエスト/レスポンスパターンの場合に適する。

▼ モデリングフレームワーク

  • SUDOモデリング (システム関連図、ユースケース図、ドメインモデル図、オブジェクト図)
  • RDRA
  • ICONIX (ユースケース駆動)

▼ テーブル構造

id order_name
1 foo
2 bar
3 baz
4 qux
5 quux


イベントソーシングパターン

▼ イベントソーシングパターンとは

ドメインモデルの状態の履歴を永続化する。

通信方式がパブリッシュ/サブスクライブパターンの場合に適する。

CQRSと相性が良い。

▼ モデリングフレームワーク

  • イベントストーミング

▼ テーブル構造

id event_name event_entity_name event_entity_id event_data
1 OrderCreated Order 1 OrderCreatedオブジェクトをJSONに変換したもの
2 OrderUpdated Order 1 OrderUpdatedオブジェクトをJSONに変換したもの
3 OrderCreated Order 2 OrderCreatedオブジェクトをJSONに変換したもの
4 OrderCanceled Order 1 OrderCanceledオブジェクトをJSONに変換したもの
5 OrderCreated Order 3 OrderCreatedオブジェクトをJSONに変換したもの
... ... ... ... ...

イベントソーシングは、ステートソーシング (CRUD) とは異なり、データの参照/更新しかない。

event_entity_idカラムを使えば、特定のドメインモデル (event_entityカラム) の状態の履歴を追跡できる。

そのため、ステートソーシングではドメインモデルの最新の状態しか永続化できない一方で、イベントソーシングパターンはドメインモデルの状態の履歴が残る。

そのため、Gitのようにして過去の状態をを参照できる