コンテンツにスキップ

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

はじめに

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


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

▼ 例:監視SaaS

プラットフォームエンジニアリングでは、エンジニアの技術的な業務がドメインになる。

例えば、監視SaaSでは監視業務領域がドメインになる。

適切な監視手法の選択、適切なTCP/IPのプロトコルの選択、アラートの巡回が業務である。

▼ 例:オンライン書店

  • カタログ管理
  • 在庫管理
  • 顧客分析
  • 決済管理

▼ 例:病院システム

  • 情報管理
  • 予約管理


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

▼ コアドメイン、サブドメインとは

core-domain_sub-domain_bounded-context

▼ ドメインエキスパートとは

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

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

*具体例*

業務アプリケーションの開発では、ビジネス業務(決済、請求、送金など)がドメインになる。

実際にビジネス側の人がドメインエキスパートを担ってもよいが、プロジェクトマネージャーがドメインエキスパートになるほうが現実的である。

*具体例*

プラットフォームエンジニアリングでは、エンジニアの技術的な業務 (監視業務、セキュリティ対策など) がドメインになる。

そのため、プラットフォームSaaSではドメインエキスパートはエンジニア(SREなど)になる。

▼ サブドメインの委譲

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

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

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

▼ 例:完全個室ジムを運営するハコジム

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

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

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

hacogym_subdomain

*例*

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

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

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


ユビキタス言語

▼ ユビキタス言語とは

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

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

domain-model

▼ 抽出例

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


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

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

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

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

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

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

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

▼ 考え方

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

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

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

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

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

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

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

反対にモノリスだと、1つの商品ドメインモデルにコンテキスト関係なく全ての種類の状態を持たせて、全てのコンテキストで再利用する。

*表*

ユビキタス言語 マイクロサービス名 マイクロサービス内のエンティティ

▼ 例:書籍ECサイト

書籍ECサイトの例。

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

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

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

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

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

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

*表*

ユビキタス言語 マイクロサービス名 マイクロサービス内のエンティティ

book_ec_ubiquitous_language

書籍モデルの実装例は次のとおりである。

/** カタログコンテキストの書籍モデル */
class Book {
  constructor(
    /** 書籍の一意の識別子 */
    public isbn: string,
    /** 書籍のタイトル */
    public title: string,
    /** 書籍の著者 */
    public author: string,
    /** 書籍の説明 */
    public description: string,
    /** 書籍の販売価格 */
    public price: number,
  ) {}
}
/** 在庫コンテキストの書籍モデル */
class Book {
  constructor(
    /** 書籍の一意の識別子 */
    public isbn: string,
    /** 書籍のタイトル */
    public title: string,
    /** 書籍の在庫数 */
    public quantityAvailable: number,
    /** 保管場所 */
    public location: string,
    /** 書籍の在庫ステータス */
    public status: "IN_STOCK" | "OUT_OF_STOCK",
  ) {}
}

▼ 例:完全個室ジムを運営するハコジム

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

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

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

*表*

ユビキタス言語 マイクロサービス名 マイクロサービス内のエンティティ

hacogym_bounded-context

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

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

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

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

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

*表*

ユビキタス言語 マイクロサービス名 マイクロサービス内のエンティティ

contract_billing_management_ubiquitous_language

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

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

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

freee_bounded-context

▼ 送金系

*表*

ユビキタス言語 マイクロサービス名 マイクロサービス内のエンティティ
送金 remittance Remittance(送金)、RemittanceHistories(送金履歴)、RemittanceStatuses(送金ステータス)
請求 payment Payment(請求)、TransactionHistory(取引履歴)
口座 account BankAccount(銀行口座)
精算 settlement Settlement(精算)
レポーティング reporting RemittanceHistory(送金履歴)、RemittanceDetailFile(送金明細ファイル)
送金管理 web-portal webポータルのフロントエンドアプリケーション

▼ QR決済系

*表*

ユビキタス言語 マイクロサービス名 マイクロサービス内のエンティティ
取引 transaction Transaction(取引)
契約 merchant Merchant(契約)
精算 payment Payment(清算)
取引管理 web-portal webポータルのフロントエンドアプリケーション


ドメインモデルの種類

▼ 隠蔽モデル

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

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処理をインフラストラクチャ層として分割することによって、対処された。

ドメイン駆動設計


ドメイン駆動設計アーキテクチャの種類

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

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

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

  • インターフェース層 (コントローラー、認証)
  • ユースケース層 (ユースケース)
  • ドメイン層 (ドメインモデル、認可)
  • インフラストラクチャ層 (永続化)

layered-architecture

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

MVCアーキテクチャ レイヤードアーキテクチャ
Model ドメイン層、インフラストラクチャ層
Controller インターフェース層
View インターフェース層
- ユースケース層 (新しく実装する)

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

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

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

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

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

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

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

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

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

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

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と相性が良い。

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

  • イベントストーミング

▼ テーブル構造

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

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

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

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

テーブル構造は以下のとおりになり、イベントデータが参照/作成されるだけである。

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に変換したもの
... ... ... ... ...