コンテンツにスキップ

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

はじめに

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


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

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

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

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

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


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

戦略的設計の手順例

ddd_strategic_design_flow

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

(1)

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

(2)

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

(3)

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

(4)

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


戦術的設計の手順例

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

(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. Amazonで商品を買う人は、商品オブジェクトが持つ機能/値段/発送状況に関するデータに関心がある。
  2. 受注管理する人は、商品オブジェクトが持つ注文内容/値段に関するデータに関心がある。
  3. 在庫管理する人は、商品オブジェクトが持つ在庫数に関するデータに関心がある。
  4. 発送管理する人は、商品オブジェクトが持つ発送状況に関するデータに関心がある。

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

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

上記の1-4は、同じ商品オブジェクトに対して関心の内容が異なるので、境界付けられたコンテキストが異なるとみなした方が良い。

別々のマイクロサービスとして分割し、それぞれのマイクロサービス上で異なる商品オブジェクトを定義し、異なるデータを持たせることになる。

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


コンテキストマップ

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

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

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

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

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

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

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


DDDデザインパターン

▼ DDDデザインパターン集


ドメインモデル図

▼ ドメインモデル図とは

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

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

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

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

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

core-domain_sub-domain_bounded-context_modeling

▼ 記法

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

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

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

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

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

*例*

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

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

(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