突然ですが、エンジニアの皆さま、Javaで開発したWebアプリケーションの構成、このようになっていませんか?
- データとgetter/setterだけのオブジェクト(JavaBean)
- 画面のコントロールやビジネスロジックの処理はServletが行う
- データベースのアクセスは、DAO(Data Access Object)に任せる
もしそうであれば、そのシステム、ドメインモデル貧血症に陥ってます。
これは、データとgetter/setterだけのオブジェクトを、Anemic(貧血症になって元気がない)オブジェクトと称し、オブジェクトとはいうものの実質的にはデータであり、それをやりとりするだけの手続き型システムなっていることを嘆いたものです。
今回は、本来のオブジェクト指向に立ち返り、そのメリットである高い保守性、再利用性、拡張性を備えた変化に強いシステムを作るための設計方法、ドメイン駆動設計について次の観点で解説します。
※ここでは、システムを情報システム、あるいは、ソフトウェアという意味で使います。
【関連記事】
ドメイン駆動設計の実装
ドメイン駆動設計(DDD)とは何か?
ドメイン駆動設計(DDD)は、エリック・エバンスが2003年に出版したエリック・エヴァンスのドメイン駆動設計という書籍で有名になりました。
ドメイン駆動設計(DDD)は、海外では非常に評判の高い書籍です。
本書の出版前から、オブジェクト指向エンジニアであり、書籍、アナリシスパターン―再利用可能なオブジェクトモデル (Object Technology Series)で有名なマーティン・ファウラーにより「期待できる内容だ」と推薦されていたようです。
また、オブジェクト指向における再利用のためのデザインパターン、通称、GoFのデザインパターンの著者の1人であるラルフ・ジョンソンは、自身のブログで、「4、5回は読みました」と賛辞を送っています。
さらに、Spring Frameworkの開発者ロッド・ジョンソンも、「JavaEEの開発者がこれから進むべき道はリッチなドメインモデルだ」と発表しています。
このエリック・エヴァンスのドメイン駆動設計は、オブジェクト指向コミュニティの間で長年培われてきたドメインモデリングのベストプラクティスを集大成した設計思想であり、エンジニアが設計上の判断を下すための枠組みと、ボキャブラリを提供するものです。
なので、ドメイン駆動設計(DDD)を、アジャイル開発で実践的に用いる方法を説いた実践ドメイン駆動設計 (Object Oriented SELECTION)も参考にして、ドメイン駆動設計(DDD)について解説したいと思います。
さて、ドメイン駆動設計(DDD)のドメインですが、これは、広義的には問題解決の対象領域という意味で、次のように分けることができます。
- 問題領域
解決すべき問題が潜んでいる領域。
問題を分析して解決すべき問題を課題として設定します。 - 解決領域
システムを使って、どのように課題を解決するか、その解決方法(ソリューション)により構成された領域。
課題をどのように解決するか設計します。
このドメインの型(モデル)をドメインモデルといい、ドメインモデルを中心に据えて開発を進めることで、より価値の高いシステムを創るための方法がドメイン駆動設計(DDD)なのです。
【ドメインモデルの例】出典:Wikipedia
さて、ドメイン駆動設計(DDD)で開発した場合、Webアプリケーションの構成は、次のようなイメージになります。
先ほど見たドメインモデル貧血症の構成と違って、さまざまな処理を担うファット(Fat)なServlet(MVCではFatControllerといいます)が中心ではなく、ビジネスロジックを担うドメインモデルが中心になっていることがわかると思います。
ドメインモデルこそ、ビジネスの姿を写し出した魂なのです。
なぜドメイン駆動設計(DDD)なのか?
先ほど言及したように、ドメイン駆動設計(DDD)の目的は、より価値の高いシステムを創ることです。
最初に、ドメイン駆動設計(DDD)によって、本来のオブジェクト指向に立ち返り、そのメリットである高い保守性、再利用性、拡張性を備えた変化に強いシステムを作ることができると述べましたが、これは、システムとしての価値です。
しかし、ドメイン駆動設計(DDD)の根本的な狙いは、ビジネス価値を提供するシステムを創り出すことにあるのです。
つまり、稼ぐ力(利益獲得能力)を持つシステムを創ることです。
どういうことでしょうか。
従来からあるユースケース駆動開発と比較して考えてみましょう。
ユースケース駆動開発は、ユースケースを中心に据えてシステムを開発する方法で、ユーザーがシステムをどう使うのかをユースケースとして洗い出し、それを内部のドメインモデルがどう実現するのか分析、設計、実装していくアプローチです。
なので、ユーザーの視点で、システムがどのようにサービスを提供するのかを考えるアプローチです。
- システムの視点
- ユーザーの視点
- アプリケーションサービスの洗練
UMLによる統一ソフトウェア開発プロセス―オブジェクト指向開発方法論 (Object oriented selection)
という書籍によると、ユースケース駆動開発の特徴は次のようになります。
- ユースケース駆動
- アーキテクチャ中心
- 反復でインクリメンタル
それに対してドメイン駆動設計(DDD)は、顧客の視点で、システムがどのようにビジネス価値を提供するのかを考えるアプローチです。
- ビジネスの視点
- 顧客の視点
- ドメインモデルの洗練
先ほどの統一ソフトウェア開発プロセスの特徴の例をかりると次のようになります。
- ドメインモデル駆動
- アーキテクチャ中心
- 反復でインクリメンタル
もう一度、それぞれの特徴を比較すると次のようになります。
- ユースケース駆動開発
- システムの視点
- ユーザーの視点
- アプリケーションサービスの洗練
- ドメイン駆動設計
- ビジネスの視点
- 顧客の視点
- ドメインモデルの洗練
このユースケース駆動開発とドメイン駆動設計ですが、これは、どちらか一方を選択するのではなく併用して進めることができます。
ユースケース駆動開発は、何を(機能)、誰が(構造)、どのように(振舞)実現するかという観点でシステムをモデル化することで開発を進めていきます。
このうち、ユースケースモデルは「機能」の部分を表し、ドメインモデルは「構造」の部分を表します。
ドメイン駆動設計(DDD)は、次のようなアプローチをとることによって、ビジネス価値を提供するシステムを創り出すことを目指すのです。
- ドメイン知識を深めながら反復的(iterative)にドメインモデルを洗練させていく
- ドメインモデルが、開発者と事業者との間の共通言語となるようにする
- ドメインモデルと実装コードとがきちんと対応付けられるようにする
ドメイン駆動設計(DDD)の構成
は次のような構成になっています。
- ドメインモデルを機能させる(Putting the Domain Model to Work)
- モデル駆動設計の構成要素(Building Blocks of a Model-Driven Design)
- 深い洞察に向けたリファクタリング(Refactoring Toward Deeper Insight)
- 戦略的設計(Strategic Design)
ドメインモデルを機能させる
ドメインモデルを機能させるためのポイントは次の3つです。
- ユビキタス言語
ドメインモデルは、ビジネスの姿を投影したものです。
なので、開発者と事業者が共通の言葉を使ってコミュニケーションし、ビジネスの姿をモデル化る必要があります。
この共通の言葉、つまり、事業者、ユーザ、設計者、プログラマーなど、プロジェクトに関わるすべての人が共通に理解できる言葉が、ユビキタス(あらゆるところで使われる)言語です。
プロジェクトを進めるときは、ユビキタス言語でビジネスをモデル化したドキュメント(ここではドメインモデル定義書と呼びます)を作成する必要があります。
ユビキタス言語は、関係者全員で理解できる自然言語と、それを補足するモデル図によって構成されます。
- モデル駆動設計
モデル駆動設計とは、開発対象のモデルをプログラムコードに展開し、プログラミングの中で得られた知識をモデルに反映するというように、モデルとコードを緊密に結びつける開発手法のことです。
モデル駆動設計を実践するには、開発ツールやオブジェクト指向プログラミングのような技術が必要になります。
ドメイン駆動設計(DDD)は、ユビキタス言語で定義されたビジネスのモデルをプログラムコードに展開するモデル駆動設計をベースとしています。
- 実践的モデラー
ドメイン駆動設計(DDD)を実現するためには、ドメインモデルを定義するモデラーがプログラマーとなり、モデルをプログラムコードに展開できる必要があります。
もしモデラーがプログラムを書けなかったり、プログラマーがドメインモデルに興味を示さなかったら、モデル駆動設計の利点であるモデリングとプログラミングの好循環は生まれません。
つまり、チームのすべての開発者は、プログラムを書く実践的モデラーでなければならないということです。
モデル駆動設計の構成要素
ドメインモデル駆動設計は、
- ドメインモデル
- モジュール
- アプリケーションアーキテクチャ
という要素から構成されます。
ドメインモデルの構成要素は、ソフトウェアの物理的な単位であるモジュールに配分されます。
モジュールは、Javaのパッケージや.NETの名前空間として実装されます。
そして、ドメインモデルやモジュールを支える設計思想および基本構造がアプリケーションアーキテクチャです。
ドメインモデル
まず、ドメインモデルから見ていきましょう。
ドメインモデルは、
ドメインオブジェクト
ドメインオブジェクトは次の要素から構成されます。
- エンティティ
- 値オブジェクト
事物の性質を表現するもので、等価性(同じオブジェクトは等価である)と不変性(状態を変えない)を持つオブジェクトのことです。
値オブジェクトは、状態を変えないので、そのメソッドは参照透過です。
私たちを取り巻く世界には、「色」や「量」など、物事を説明したり定量化するためのもので、特に同一性を考える必要のないオブジェクトが多く存在しています。
ドメインに、そのようなオブジェクトがある場合、値オブジェクトとして分類します。
同一性(IDを持つ)と連続性(状態が変化する)を持つオブジェクトのことです。
エンティティは、状態を変えるので、そのメソッドは参照透過ではありません。
私たちを取り巻く世界には、各個体が識別され、生成から消滅までのライフサイクルを持つオブジェクトが多く存在しています。
ドメインに、そのようなオブジェクトがある場合、エンティティとして分類します。
※参照透過
オブジェクトのメソッドが、変数値についてどこに記憶されている値を参照しているか意識しなくても、常に同じ入力に対して同じ結果を出力すること。
同一性、連続性、等価性、参照透過については、オブジェクトの性質を参照してください。
ドメインサービス
ドメインに存在する概念の中には、1つの機能が単体で存在していて、エンティティや値オブジェクトなどのクラスのメソッドに属さない場合があります。
そのような場合、その機能をドメインサービスという状態を持たない(statelessな)オブジェクトとして分類します。
ドメイン知識が必要なビジネスロジックはドメインモデルが実行し、ドメインモデルを利用する側(クライアント)にドメイン知識が流出しないように注意します。
ドメインモデルのクライアントがドメイン知識を持つと、クライアントとドメインモデルの結合度が上がり、ドメインモデルのモジュール性、再利用性が下がります。
なので、エンティティや値オブジェクトでビジネスロジックが実現できない場合は、ドメインサービスとして実現するようにします。
なお、ドメインサービスは、GRASPの純粋人工物 (Pure Fabrication)に該当します。
ドメインオブジェクトのライフサイクル
先ほど説明したように、エンティティには、生成から消滅までのライフサイクルがあります。
また、その生存期間中に、一時的にデータベース上に保管したりします。
ここでは、ドメインオブジェクトのライフサイクルを管理するための要素について説明します。
- 集約
集約とは、ライフサイクルの単位となるドメインオブジェクトのまとまりのことです。
たとえば「発注」と「発注明細」のように、関連するエンティティのグループは集約として扱い、集約を単位として他の要素との境界を明確に分けます。
集約は全体と部分から構成されますが、全体側(先の例では「発注」)を集約のルートとします。
1回きりの処理のような場合を除いて、外部から参照できるのはルートだけで、ルートが保持するドメインオブジェクトに対する処理はすべてルートが中継することで、集約内のモデルの一貫性を確保します。 - ファクトリ
集約の複雑な生成過程をカプセル化する役割がファクトリです。
ファクトリは、デザインパターンのFactoryMethodパターンやAbstractFactoryパターンに該当します。
なお、集約が、それが保持するエンティティを生成するためのファクトリメソッドもファクトリに該当します。 - リポジトリ
集約の生存期間中、それを一時的にデータベース上に保管・問い合わせするための役割がリポジトリです。
リポジトリは、DAO(Data Access Object)のようにデータをアクセスする機能ではなく、オブジェクトを保管する機能を抽象化したもので、メモリ上にあるコレクションを扱うように操作することができます。
リポジトリは、グローバルなアクセスが必要な集約のルート毎に1つ用意されます。
なお、リポジトリは、GRASPの純粋人工物 (Pure Fabrication)に該当します。
アプリケーションアーキテクチャ
ドメインモデルとコードとを結びつけるモデル駆動設計を実践するには、ドメインモデルを他の関心事(プレゼンテーション、データアクセスなど)から分離する必要があります。
ビジネスロジックがUIやデータアクセスのコードから切り離されており、独立して変更、修正できる必要があります。
ここでは、ドメインモデルを他の要素から切り離し、独立したモジュールとして扱うためのアプリケーションアーキテクチャについて解説します。
レイヤアーキテクチャ
従来のレイヤパターンを使ってドメインレイヤを分離した例がこちらになります。
- UIレイヤ
ユーザとの相互作用の境界となるレイヤです。 - アプリケーションレイヤ
ドメインモデルの構成要素を操作することで、ユースケースを実現するレイヤです。 - ドメインレイヤ
ドメインモデルのレイヤです。 - インフラストラクチャレイヤ
上の3層を支える技術的な基盤となるレイヤです。
SOLIDの依存関係逆転の原則を適用した場合、次のようになります。
ヘキサゴナルアーキテクチャ
実践ドメイン駆動設計 (Object Oriented SELECTION)
に、レイヤアーキテクチャとは別のアーキテクチャ、ヘキサゴナルアーキテクチャが紹介されています。
先のWebアプリケーションの構成のように、通常のアーキテクチャは、クライアントがシステムと対話する部分を「フロントエンド」、データを保管したり出力する部分を「バックエンド」と考えます。
ヘキサゴナルアーキテクチャは、フロントエンド(前)とバックエンド(後)という考え方をせず、ドメインモデルを中心に、「内部」と「外部」という考え方をし、どのようなクライアントがきてもアダプタを追加するだけで接続できるようにするというアプリケーションアーキテクチャです。
さまざまな型の外部要素が、それぞれ自分ようのアダプターを持っており、それがアプリケーションのAPIに合わせた形式に入力を変換します。
六角形(ヘキサゴン)の各辺が、それぞれ別の種類のポートを表し、入力あるいは出力に対応しています。
アプリケーションのAPIは、内部要素に対するインターフェースとなり、システムの機能要件ごとに設計されます。
ヘキサゴナルアーキテクチャを採用することで、ドメインモデルを中心としたドメイン駆動設計と、ユースケースを中心としたユースケース駆動開発を平行して実施することが容易になります。
アプリケーションサービス
ヘキサゴナルアーキテクチャのアプリケーションは、アプリケーションサービスとして実装することができます。
アプリケーションサービスは、ドメインモデルの直接のクライアントで、ユースケースのシナリオを実現するためにドメインモデルのタスクを調整するだけの役割を担います。
ここで、ドメイン知識が必要なビジネスロジックはドメインモデルが実行し、アプリケーションサービスにドメイン知識が流出しないように注意します。
アプリケーションサービスがドメイン知識を持つと、アプリケーションとドメインモデルの結合度が上がり、ドメインモデルのモジュール性、再利用性が下がります。
アプリケーションサービスには、ユースケースシナリオごとに一つのメソッドがあり、ACID特性を持つデータベースを扱う場合は、トランザクション制御も行います。
深い洞察に向けたリファクタリング
先に説明したようにドメイン駆動設計(DDD)は、反復的に(インクリメンタルに)進められます。
事業者やユーザの頭の中にある知識をより良く反映した「より深いモデル(deeper model)」を得るには、継続的な改善(リファクタリング)が必要です。
ここでは、より深いモデル(deeper model)を設計するための手法について解説します。
仕様(Specification)
ビジネスルールを記述する機能を特定のエンティティや値オブジェクトのメソッド(isXXという真偽値を返すメソッド)として定義すると、その責務が肥大化し、SOLIDの単一責任の原則に違反する場合があります。
また、さまざまなエンティティに共通したビジネスルールを定義したい場合もあります。
このような場合、ビジネスルールを値オブジェクトとしてカプセル化する仕様(Specification)という方法があります。
仕様(Specification)は次のようなケースに適用されます。
- 妥当性検証(validation)
オブジェクトが満たすべき条件の検証。 - 選別条件(selection)
条件を満たすオブジェクトの選択。
コレクションの中から選択します。 - 生成条件(creation)
条件を満たすオブジェクトの生成。
ビジネスルールを仕様(Specification)として隠蔽することで、それを利用するクライアントにドメイン知識が流出されなくなります。
意図の明白なインタフェース(Intention-Revealing Interfaces)
JavaBeanを適用した場合など、setXXなど機械的にメソッド名を定義する場合があります。
ドメイン駆動設計(DDD)では、メソッド名など、それを使用するクライアントに対するインターフェースは、意図が明確に伝わるものでなければならいとしています。
意図が分かりずらいということは、ビジネスの内容が正しく反映できていない可能性がありますし、クライアントが誤った使い方をし、生産性や保守性を落とす可能性があります。
なので、メソッド名などインターフェースは、ドメインモデルのユビキタス言語に従うようにし、テストファーストを実践して、それを使う側の視点で考えるようにします。
副作用の無い関数(Side-Effect-Free Functions)
例えば、次のような方法で、できるだけ副作用のない関数を適用します。
- 状態が変化しない値オブジェクトのメソッドして実現する。
- 単に引数を加工して結果を返すだけのロジック部分はできる限り副作用の無いメソッドとして実現する。
- コマンド・クエリ分離の原則(CQS)に従って、副作用のあるメソッドはコマンドとして実現する。
※副作用
関数が、引数を受け取り値を返すことによる主作用とは別に、ローカル外部の状態変数を変化させること。
副作用は、予期せぬエラーの原因となり、ソフトウェアの保守性を低下させます。
表明(Assertions)
副作用の無い関数を使って、副作用を最小限に抑えることが重要ですが、副作用をともなうコマンドを完全になくすことは困難です。
そこで、契約による設計に従って、メソッドの利用条件を明確に表明(assert)することで副作用のあるコマンドを少しでも理解しやすい形で表現するようにします。
概念の輪郭(Conceptual Contours)
ドメインモデルのリファクタリングを何度も繰り返すことで概念の適切な輪郭を見つけ出し、オブジェクトの粒度がその輪郭に沿うようにします。
これは、GRASPの高凝集を実現することになります。
GRASPの役割分担に関する原則が参考になります。
独立したクラス群(Standalone Classes)
ドメインモデルを構成するクラスのまとまりから、関係のないクラスへの依存関係を極力排除していき、その中だけで独立して理解できる単位にします。
これは、GRASPの疎結合を実現することになります。
GRASPの間接化や変動をカプセル化するための原則が参考になります。
閉じた操作(Closures of Operations)
操作の引数や戻り値に新しいクラスが登場すると、それは新たな依存関係の導入になります。
そこで、できるだけ、操作の結果が、その引数と同じ型のオブジェクトを返すような操作(閉じた操作)の導入を検討します。
閉じた操作は、引数と戻り値が同じ型になるので、そこに新たなクラスを持ってくる必要がありません。
戦略的設計
ここでは、企業全体、エンタープライズアーキテクチャ(EA)の観点でドメインモデルの位置付けを考えます。
ドメイン駆動設計(DDD)の「戦略的設計」で重要な概念は次の3つです。
- ドメイン
- コンテキスト境界
- コンテキストマップ
ドメイン
先に述べたように、広義のドメインは、問題領域と解決領域に分けることができます。
このうち、問題領域は、ビジネスアーキテクチャ(BA)の活動領域と同じと考えてよいかと思います。
ドメイン駆動設計(DDD)では、問題領域としてのドメインを以下のように分類します。
- コアドメイン
経営戦略上、重要な事業の中核となる活動領域。 - サブドメイン
コアドメインではない補助的な活動領域で、以下のように分類することができます。- 支援ドメイン
コアドメインの活動を支援する活動領域。 - 汎用ドメイン
コアドメインにとって特別な活動領域ではないが、業務活動全般として必要な活動領域。
- 支援ドメイン
著書、実践ドメイン駆動設計 (Object Oriented SELECTION)で題材となっている架空サンプルプロジェクト「SaaSOvation」のドメイン構成の例は次のようになります。
コンテキスト境界
ドメイン駆動設計(DDD)では、問題領域であるドメインに対する解決領域をコンテキスト境界(Bounded Context)といいます。
通常、1つのコアドメイン、もしくはサブドメインに、1つのコンテキスト境界が対応します。
コンテキスト境界は、ユビキタス言語の文脈(コンテキスト)にもなり、コンテキスト境界ごとにメインモデルを定義します。
なので、通常、コンテキスト境界ごとにチームが割り振られ、継続的にドメインモデルを洗練していきます。
著書、実践ドメイン駆動設計 (Object Oriented SELECTION)で題材となっている架空サンプルプロジェクト「SaaSOvation」のコンテキスト境界の例は次のようになります。
コンテキストマップ
次に、コンテキストマップ(Context Map)ですが、これは、コンテキスト境界間の関係を描いたものです。
コンテキストマップを描くことで、コンテキスト境界間の対応関係が明確になり、それら全体を、どのように統合するか設計することができます。
著書、実践ドメイン駆動設計 (Object Oriented SELECTION)で題材となっている架空サンプルプロジェクト「SaaSOvation」のコンテキストマップの例は次のようになります。
この例を見ると、サブドメインの方が上流になり、コアドメインに影響を与えることがわかります。
つまり、支援する側が、支援される側に影響を与えるわけです。
さて、著書、実践ドメイン駆動設計 (Object Oriented SELECTION)では、コンテキストマップで描くコンテキスト境界間の関係を、次の2つのパターンとして示しています。
- 組織パターン
チーム間の関係を示すパターン。 - 統合パターン
データとプログラムの連携方法を示すパターン。
【組織パターン】
組織パターンには次の4つがあります。
パートナーシップ(Partnership)
2つのチームにおいて協力的な関係が成り立つ場合、「パートナーシップ」の関係と呼びます。
「パートナーシップ」の場合、計画から連携試験まで共同でマネジメントを行います。
チーム間のインターフェイスを共に検討し、最適な状態になるよう発展させます。
顧客/供給者(Customer/Supplier)
2つのチーム間において、使う側(下流)と使われる側(上流)になっている場合、それを顧客と供給者の関係と捉え、「顧客/供給者」の関係と呼びます。
供給者(上流)側チームの成否が顧客(下流)側チームの成否に影響を大きく与える場合、供給者(上流)のチームが顧客(下流)のチームに対して適切なサポートを行います。
なので、顧客(下流)側チームの要件も考慮して、供給者(上流)側チームが計画や予算を立案します。
順応者(Conformist)
2つのチーム間において、「顧客/供給者」の関係のように上流側が下流側の要望を考慮することがない場合、「順応者」の関係と呼びます。
上流側は下流側の都合を考慮しないため、下流側チームは「腐敗防止層(後述)」を用意して、上流側モデルの変換処理を行います。
例えば、GoogleのAPIを使用するアプリを開発する場合、APIの変更があったとしても、アプリ開発側で対応するしか無いので、アプリ開発側が「順応者」になります。
別々の道(Separate Ways)
2つのコンテキスト境界を連携するメリットがない場合、「別々の道」として、コンテキスト間で統合を行わないことを明示します。
【統合パターン】
複数のコンテキスト境界において、共有できる核(カーネル)となる部分がある場合、それをを共有します。
共有される核(カーネル)の部分には、ドメインモデル、ソースコード、DBスキーマなどが含まれます。
共有カーネルに変更が必要な場合、他のチームの承認が必要となるので、その分、マネジメントのコストがかかります。
公開ホストサービス(OHS:Open Host Service)
コンテキスト境界の内容に外部からアクセスできるサービスを公開するパターンです。
最近ではREST形式のWebサービスとして公開する方法が主流となっています。
公表された言語(PL:Published Language)
2つのコンテキスト境界内のモデルを変換する場合、共通の言語が必要となります。これを「公表された言語」と呼びます。
最近ではJSONとXMLがよく使用されています。
「公開ホストサービス」と組み合わして使用されることが一般的です。
腐敗防止層(ACL:Anti Corruption Layer)
チーム間の関係が「順応者」の場合、上流側は下流側の都合を考慮しないため、下流側チームは上流側を隔離するための「腐敗防止層」を用意して、上流側モデルの変換処理を行います。
腐敗防止層の構築には大きなコストがかかるので注意が必要です。
巨大な泥団子
これまで見てきたように、コンテキスト境界は適切な大きさに分割して管理します。
しかし、例えば、レガシーシステムなど、既存システムが大規模で複雑な場合、そのまま大きな固まり「巨大な泥団子」として捉えます。
「巨大な泥団子」が見つかったら、きれいにモデリングするなど考えず、それが自分たちのコンテキスト境界を侵食してこないよう注意します。
さて、著書、実践ドメイン駆動設計 (Object Oriented SELECTION)で題材となっている架空サンプルプロジェクト「SaaSOvation」の統合例は次のようになります。
どのようにドメイン駆動設計(DDD)を進めるか?
次の図のように、ドメイン駆動設計(DDD)とユースケース駆動開発は縦と横の関係で、互いに協調しながら反復的に進めていきます。
ドメイン駆動設計(DDD)は、エンタープライズアーキテクチャ(EA)のアプリケーションアーキテクチャ(AA)をベースとして進めます。
アプリケーションアーキテクチャ(AA)の設計は、次の手順で進めます。
- 企業全体のドメイン(問題領域)分析
- 企業全体のコンテキスト境界分析
- 企業全体のコンテキストマップ設計
企業全体のドメイン(問題領域)分析
まず、ビジネスアーキテクチャ(BA)の活動領域に基づいて、アプリケーションシステムのドメイン(問題領域)を定義します。
バリューチェーンとドメインの関係は以下のように考えることができます。
- バリューチェーンの主要活動の中でシステムが関係する活動がコアドメイン
- バリューチェーンの事業支援活動の中でシステムが関係する活動が支援ドメイン
- バリューチェーンの企業支援活動の中でシステムが関係する活動が汎用ドメイン
企業全体のコンテキスト境界分析
次に、企業全体のドメイン(問題領域)をベースに、コンテキスト境界を設定します。
この例では、青い丸で囲んだ部分が一つのコンテキスト境界を表しています。
コンテキスト境界に対応する現行システムを明確にします。
企業全体のコンテキストマップ設計
最後に、企業全体のコンテキスト境界の関係をコンテキストマップとして描きます。
それでは、個々のアプリケーションのドメイン駆動設計(DDD)の流れを順番に見ていきましょう。
ドメイン駆動設計(DDD)は、エンタープライズアーキテクチャ(EA)のビジネスアーキテクチャ(BA)とアプリケーションアーキテクチャ(AA)を受けて設計します。
戦略的設計
ドメイン駆動設計(DDD)の戦略的設計は、アプリケーションアーキテクチャ(AA)をベースとして、次のような手順で行います。
- ドメイン(問題領域)分析
- コンテキスト境界分析
- コンテキストマップ設計
- アーキテクチャ設計
- モジュール設計
ドメイン(問題領域)分析
まず、アプリケーションアーキテクチャ(企業全体)のドメイン(問題領域)分析結果を受けて、アプリケーション単位のドメイン(問題領域)を定義します。
現在、米国を中心に人気のあるフィットネススタートアップの「Peloton」の例で考えてみましょう。
Pelotonについての詳細は、ビジネスアーキテクチャ(BA)を参照してください。
Pelotonのバリューチェーン
Pelotonのバリューストラクチャ
- 顧客がPelotonストアでフィットネスバイクを購入すると、配送員がサービスの一環としてセットアップをしてくれる。
- キーリソースであるインストラクターが出演するライブを映像ディレクターが制作してフィットネスバイクへ配信する。
- 顧客は、それを見て、楽しく、手軽にワークアウトする。
- プロダクトデザイナーが新規機能を開発すると、それがフィットネスバイクに配信され自動アップグレードされる。
- 顧客は、ユーザーコミュニティに参加して体験を共有することで、ますますPelotonのファンになる。
Pelotonのフィットネスバイク製造販売事業のバリューチェーンから設定した、アプリケーションのドメイン(問題領域)の例は以下のようになります。
顧客のワークアウトがコアドメインで、製品の開発・改良、ライブ動画の制作、アフターサービスが支援ドメイン、顧客管理が汎用ドメインになります。
コンテキスト境界分析
次に、アプリケーションアーキテクチャ(企業全体)のコンテキスト境界分析の結果も参考にして、アプリケーション単位のコンテキスト境界を定義します。
先ほどのドメイン(問題領域)に対してコンテキスト境界を割り当てると次のようになります。
今回は、顧客のワークアウトを管理するワークアプト管理システムを開発するものとします。
コンテキストマップ設計
次に、定義されたコンテキスト境界同士の関係を考えてコンテクストマップを設計します。
ワークアプト管理システムのコンテクストマップの例は以下のようになります。
それぞれの関係は以下のようになります。
- ワークアウト管理システムは、製品開発コンテキストのシステムから最新の製品情報を受け取ります。
- ワークアウト管理システムは、ライブ映像制作コンテキストのシステムからライブ映像を受け取ります。
- ワークアウト管理システムは、顧客管理コンテキストのシステムから顧客情報を受け取ります。
- ワークアウト管理システムは、アフターサービスコンテキストのシステムに顧客のワークアウト情報を提供します。
アーキテクチャ設計
次に、ワークアプト管理システムのアーキテクチャを設計します。
前述したヘキサゴナルアーキテクチャを適用することにします。
また、コマンド・クエリ責務分離(CQRS)パターンも適用することにします。
モジュール設計
ワークアプト管理システムのアーキテクチャを考慮して、ワークアプト管理システムのモジュール構成を設計します。
Java言語で開発する場合、モジュール構成はJavaのパッケージ構成になります。
戦術的設計
戦術的設計は、次の手順で行います。
- ドメインモデル分析
ドメイン(問題領域)のドメインモデル(分析モデル)を作成します。 - ドメインモデル設計
コンテキスト境界(解決領域)のドメインモデル(設計モデル)を作成します。
ドメインモデル分析
まず、ドメイン(問題領域)の概念構造を分析モデルとして表します。
そのために、ビジネスアーキテクチャ(BA)のバリューストラクチャ、業務構造を
業務の説明
分析モデル(図)
に展開し、ドメインモデル定義書に記述します。
Pelotonの例で見てみましょう。
Pelotonのバリューストラクチャのうち「顧客のワークアウトドメインに関係する部分は以下のようになります。
これより、以下のような分析モデル(図)を描くことができます。
業務の骨組みは以下のようになります。
- 顧客は複数のフィットネスバイクを所有する。
- 顧客は、配信されたライブ映像を見ながらワークアウトする。
この骨組みの部分に対して、顧客が選択できるワークアウトスタイルの種類であるとか、一度に配信されるライブ映像の数であるとか、ビジネスルールの詳細を肉付けしていきます。
さて、Pelotonの事業ドメインを確認してみましょう。
Pelotonの事業は、楽しみながら継続したいという顧客に対して、時間と場所に不自由しない新しいフィットネス体験という価値を提供することです。
なので、「時間と場所に不自由しない新しいフィットネス体験という価値の提供」を実現するための問題と課題を考えて、それをドメインモデルに反映することが最も重要なことです。
ドメインモデル設計
次に、ドメイン(問題領域)の分析モデルを、システムでどう実現するかという観点でコンテキスト境界の設計モデルに展開します。
設計モデルは、設計モデル(図)で表し、テストファーストで、それを実装して検証します。
Pelotonの「ワークアウトコンテキスト」の設計モデル(図)の例は次のようになります。
エンティティ、値オブジェクト、集約のルートをステレオタイプとして識別しています。
さきほどドメイン分析で設定された「時間と場所に不自由しない新しいフィットネス体験という価値の提供」を実現するための課題をソフトウェアでどう解決できるか、これがドメインモデル設計の設計思想になります。
以上、今回は、ドメイン駆動設計(DDD)について解説しました。
動画視聴もできます!
[…] ドメインモデル駆動設計でいうドメインオブジェクトの種類でいうと、マスターデータは同一性を持つエンティティになりますが、参照データは、同一性を持たない値オブジェクトと同 […]
[…] 分析のときに適用したBCEを、設計でMVCに変換する場合、次のような例が考えられます。 この例では、Spring MVCのJSPやServletが適用されています。 さて、先程、サブシステムをレイア構造で設計しましたが、これもレイアパターンというアーキテクチャパターンになります。 書籍「実践ドメイン駆動設」では、ドメインモデルを中心にしたヘキサゴナルアーキテクチャというパターンが紹介されています。 ヘキサゴナルアーキテクチャは、フロントエンド(前)とバックエンド(後)という考え方をせず、ドメインモデルを中心に、「内部」と「外部」という考え方をし、どのようなクライアントがきてもアダプタを追加するだけで接続できるようにするというアプリケーションアーキテクチャです。 さまざまな型の外部要素が、それぞれ自分ようのアダプターを持っており、それがアプリケーションのAPIに合わせた形式に入力を変換します。 六角形(ヘキサゴン)の各辺が、それぞれ別の種類のポートを表し、入力あるいは出力に対応しています。 アプリケーションのAPIは、内部要素に対するインターフェースとなり、システムの機能要件ごとに設計されます。 ヘキサゴナルアーキテクチャを採用することで、ドメインモデルを中心としたドメイン駆動設計と、ユースケースを中心としたユースケース駆動開発を平行して実施することが容易になります。 なお、多くの汎用メカニズムは、アプリケーションフレームワークに内包されているので、アプリケーションフレームワークを使うことによって比較的簡単に適用することができます。 […]
[…] バリューストラクチャは、データアーキテクチャの全体概念データモデル、アプリケーションアーキテクチャのコンテキストマップに展開することができます。 次の図は、俺のフレンチのバリューストラクチャをコンテキストマップに展開した例です。 図中のUはUpstream(上流)の略で、モデルの変更が相手に影響を与える側で、DはDownstream(下流)の略で相手のモデルの変更の影響を受ける側です。 アプリケーションアーキテクチャでは、このコンテキストマップを受けて、上流と下流をどう統合するのか設計します。 データアーキテクチャの場合、バリューストラクチャが全体概念データモデルになるので、それを構成する活動領域(ジョブの単位と同じ)ごとに概念データモデルを設計します。 次の図は、販売領域の概念データモデルの例です。 以上、ここでは「俺のフレンチ」のビジネスアーキテクチャについて紹介しました。 […]
[…] ここでは、ヘキサゴナルアーキテクチャを適用したドメイン駆動設計を、Javaを適用してどう実現するのかについて次の観点で解説します。 […]
[…] 詳細は、ドメイン駆動設計・戦略的#…を参照してください。 […]
[…] ドメイン駆動設計の集約のルートにする 例えば、受注と受注明細が強& […]
[…] 統一ソフトウェア開発プロセスとは、UML(統一ソフトウェア開発言語)を開発したグラディ・ブーチ(Grady Booch)、ジェームズ・ランボー(James E. Rumbaugh)、イヴァー・ヤコブソン(Ivar Jacobson)を中心に開発されたソフトウェア開発プロセスです。 ブーチ、ランボー、ヤコブソン(スリーアミーゴという愛称で親しまれています)は、それぞれ独自に提唱していたオブジェクト指向開発の方法論のうち、表記法をUML(Unified Modeling Language)、開発プロセスをUP(Unified Process)として統一しました。 なお、本記事は、書籍「UMLによる統一ソフトウェア開発プロセス」を参考にしています。 それから、アプリケーションを設計する重要な考え方としてドメイン駆動設計があるので、合わせて参照してください。 […]
[…] ;値を上げることができる ドメイン駆動設計 (DDD)は、事業のドメイン(ビ&# […]
[…] ヴォーン・ヴァーノンの実践ドメイン駆動設計という書籍では、ドメイン駆動設計(DDD)の構成要素である集約ごとにイベントストアを設け、イベントソーシングを実現することを推奨しています。 また、Sam Newmanのマイクロサービスアーキテクチャという書籍では、DDDの集約ごとにマイクロサービスを設けることにより、マイクロサービスの一貫性を保ちやすく、依存関係を最小限にできると説明しています。 集約は、ドメインオブジェクトのライフサイクルを管理する単位であるので、この単位にコマンド処理のマイクロサービスを設け、イベントソーシングによって集約のイベントを管理することで、イベントソーシングのメリットである独立性、拡張性、可用性などを高めることができます。 なお、集約の状態を変化させる操作など、特定のドメインにおける重要な出来事のことをドメインイベントといいます。 複数のマイクロサービスを跨いだ分散トランザクションは、Sagaによって管理することができます。 […]