コンテキスト境界を使ってドメイン駆動設計の効果を高める:ドメイン駆動設計の決め手、コンテキスト境界
ドメイン駆動設計は、ビジネスの主要ニーズに重点を置いてソフトウェアを開発するのに役立つ。だが、ドメイン駆動設計を実践するには、コンテキスト境界についての基礎を理解する必要がある。
アーキテクトは、純粋なドメイン駆動設計(DDD:Domain-driven design)を自社に導入するのに苦労することが多い。DDDでは、複数の部署や複数のソフトウェアシステムにまたがってビジネスプロセスを対応付ける必要があるため、その複雑さから目的の成果を得るのが難しくなる。
とはいえ、ドメインモデリングはビジネスニーズを技術設計に置き換えるのに優れたアプローチだ。そのため、アーキテクトは、DDDの効果の決め手となるコンテキスト境界(bounded contexts)を定める方法を理解する必要がある。
コンテキスト境界とは
ソフトウェア設計コンサルタントで書籍も執筆しているエリック・エヴァンス氏は、自身の著書『Domain-Driven Design:Tackling Complexity in the Heart of Software』(※)でコンテキスト境界について定義している。初めにエヴァンス氏は、複数のドメインからなる大きなビジネスドメインを、各ドメインをサポートする個別の基盤ソフトウェアシステムへ対応付ける際に、適切なコンテキスト境界がいかに役立つかを説明している。その意味では、コンテキスト境界とは、大きなソフトウェアシステムの複雑さを抑えるために必要なルールと境界を手引きするパターンだといえる。
(※)邦題:『エリック・エヴァンスのドメイン駆動設計、ソフトウェアの核心にある複雑さに立ち向かう』翔泳社
コンテキスト境界により、定義するドメインモデルが意味を成すことが保証される。コンテキスト境界の境目がどこになるかはそのシステムを利用する人々にとって明らかなはずだ。この境目が明確でなければ、非効率で複雑なドメインモデルが生み出される可能性がある。
コンテキスト境界の詳細を伝える最も効果的な方法の一つは、識別名を確立し、それを一貫して使用することだ。例えば、ユーザー、顧客、管理者、製品の定義を明確に表現すれば、チームはそれぞれの言葉の意味とシステム内でのそれぞれの関係を明確に理解できるようになる。そうすれば、同じ概念を異なる言葉で表現することがなくなる。同じ概念を違う言葉で表現すると、大きなシステムを担当するチームは瞬く間に混乱に陥ることになる。
コンテキスト境界を対応付ける際の方針
コンテキスト境界をシステム全体で対応付ける方法は幾つかある。
共有カーネル(Shared kernel)
共有カーネルとは、2つの異なるコンテキスト境界の基盤となるコードを2つ以上のチームが共有するパターンを指す。つまり、複数のチームが互いに共通ドメインオブジェクトに依存することになる。モデルの目的が時間の経過と共に変わってしまうことがないように、ドメインモデルを変更する計画をチーム同士で話し合っておかなければならない。
順応者(Conformist)
ドメインモデルを1つのチームが主体となって管理する場合、モデルを下流で利用する利用者は、上流システムのドメインモデルにそのまま準拠できるかどうかを評価しなければならない。下流の利用者が「モデルを利用するか、放棄するか」を決める姿勢を順応者アプローチと呼ぶ。
腐敗防止層(Anti-corruption layer)
下流の利用者が上流のドメインモデルをそのまま受け入れることができない場合、上流のコンテキストを下流独自の境界コンテキストに変換する変換層を追加することができる。この変換層を腐敗防止層(ACL:anti-corruption layer)と呼ぶことが多い。ACLは、コンテキスト境界の対応付けを主に担当する。
公開ホストサービス(Open host service)
ドメインモデルが、上流サービスよりも下流の利用者の影響を強く受ける場合、「逆方向」のACLプロセスに従うことができる。つまり、下流のサービスがACLを作成するのではなく、上流サービスが公開仕様を発行することができる。この公開仕様は、内部コンテキストとは異なる方法で記述し、内部のドメインモデルと公開仕様を対応付ける。そうすることで、下流の利用者に直接影響を与えることなく、実装を改良できる。
ドメインモデルにおけるオブジェクトの種類の定義
ここまでは、さまざまなパターンを当てはめて、大きなソフトウェアシステム全体で境界コンテキストを対応付ける方法について見てきた。ただし、各境界コンテキスト内では、コンテキスト自体とユビキタス言語を定義することが不可欠だ。ユビキタス言語とは、境界コンテキストとドメインモデルを定義する共通言語のことをいう。ドメイン内のさまざまな種類のオブジェクトの対応付けを済ませれば、境界コンテキストのガイドラインの中でビジネスモデルを対応付けるドメインモデルを構築できる。
エンティティ(Entity)
共通属性を共有するオブジェクトであっても、それぞれ独自の一意IDを保持しなければならない場合がある。そのような場合、基本属性の記述以外の方法でドメイン内のオブジェクトを識別しなければならない。そのオブジェクト独自のIDを定義する必要のあるオブジェクトを「エンティティ」と呼ぶ。このエンティティが、ドメインモデルのコアビルディングブロックを構成する。
例えば、ユーザーをユーザー名で選択するソフトウェアシステムがあり、別のユーザーが自分と同じユーザー名を既に使用しているとする。混乱を避けるため、このシステム内では両ユーザーは区別できる別のIDを持つ必要がある。
値オブジェクト(Value objects)
DDDでは、エンティティは独自の一意IDを持つ要素を指す。DDDにはもう1つ値オブジェクトという要素の種類がある。DDDでは不可欠な値オブジェクトは、変更不可の要素で、ドメインモデル内では一意の自己完結型の要素を表す。値オブジェクトでは属性や特性がカプセル化されるが、独自のIDは持たず、属性で定義される。値オブジェクトの例を以下に幾つか示す。
- 金銭(通貨と金額で定義)
- 日付範囲(開始日と終了日で定義)
- 住所(番地、都市、郵便番号で定義)
値オブジェクトは変更不可なため、一貫性を確保し、ドメインモデル内で予期しない変更が行われるのを防ぐのに適している。変更が必要な場合は、システム内での整合性を確保するため、既存のインスタンスを変更するのではなく、新しいインスタンスを作成する。
集約(Aggregates)
複数のオブジェクトのライフサイクルと状態が緊密に結び付いている場合がある。ただし、一貫性を確保するには、緊密に結び付く複数のオブジェクトをまとめて検証したり、変更を拒否したりできることが不可欠だ。このような場合、複数のオブジェクトをまとめて表現できる抽象表現を見つけると役立つ。こうした抽象表現を決めれば、その表現をルート集約として使用して1つのトランザクション内で全てのオブジェクトを変更することができる。その結果、ドメイン全体で求める一貫性が確保される。
システムでの集約の例として、Eコマースシステムを考える。このシステムでは、「注文」が集約になる可能性がある。「注文」には、通常、「商品」「顧客情報」「配送先住所」「決済方法の詳細」のような要素が含まれる。「注文」がメインの集約となり、他の全ての要素を結び付ける。「注文」には、「注文ID」「注文日」「ステータス」のような情報が含まれる。「顧客情報」には、注文を行う顧客についての情報を記録する。「配送先住所」は、注文品の届け先住所になる。「決済方法の詳細」には、支払方法、支払状況、クレジットカード情報など、顧客が注文を決済する方法についての情報が含まれる。
このシナリオでは、「注文」が関連する情報を全てまとめる集約ルートの役割を果たす。「注文」によって、全ての要素の一貫性が確保され、全ての要素がビジネスルール(商品在庫の確保、決済方法の検証など)に従うことが保証される。
集約にグループ化することは、複雑さを抑え、一貫性を確保して、ドメインモデル内の境界を明確に定義するのに役立つ。
その他の種類のオブジェクト
オブジェクトには、エンティティ、値オブジェクト、集約以外にも、ファクトリ(factories)、サービス(services)、リポジトリ(repositories)のような種類もある。これらはドメインモデルという枠組みの中の共通要素で、構築、オーケストレーション、状態保存の機能を提供する。
アンチパターンに注意
コンテキスト境界を管理する際には注意すべき点が幾つかある。
貧血(Anemic)オブジェクト
ドメインモデル内のオブジェクトが適切な名前を持ち、関係が正しく定義されていることから一見正当なオブジェクトのように見えることがある。ただし、詳しく調べていくと、一貫した状態は確保されていても、動作に一貫性がないことが分かる場合がある。このような「貧血(anemic)」オブジェクトを見極めることは、ドメインをシンプルにし、モデルから不必要な抽象表現を取り除くための1つのステップとなる。
偽同族語(False cognates)
偽同族語とは、チームメンバーがドメインモデルの同じ概念について話していると考えているのに、実は違うことについて話していることを指す。そうなると、ドメインモデルに矛盾した要求が行われ始める恐れがある。ドメインモデルをシンプルに保つには、設計プロセスの早い段階で概念、区別、想定を明確に特定しておくことが重要だ。
概念の重複
偽同族語に多少関係するが、コンテキスト境界内の2つの異なるドメインモデルを誤って1つの概念として作成してしまうことがある。両ドメインモデルのルールが微妙に異なれば、一方のドメインモデルを変更することで、複数のドメインモデルの更新が必要になる恐れがある。ドメイン内の動作を統一し、全体的な複雑さを軽減するには、両ドメインモデルの重複を特定することが重要だ。
コンテキスト境界でのコードの再利用
ドメインモデルが1対1に対応付けられているように見えることから、2つのコンテキスト境界の間でコードを再利用したいと考えることがある。だが、この考え方は危険だ。ドメインモデルはそれぞれのコンテキスト境界内で異なる目的へと進化する可能性があり、それぞれのコンテキスト境界が進化するにつれて異なっていき、両者が競合する可能性があるためだ。モデリング全体をクリーンかつシンプルに保つため、独立性を維持するのが賢明だ。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- マイクロサービスアーキテクチャにおける「サービス配置」の考え方
マイクロサービスアーキテクチャを用いてシステム開発をする場合、アプリケーションをどのように分割して配置すればいいのか。アプリケーション間の通信はどのような点に留意すべきかを、オイシックス・ラ・大地の川上徹氏が解説します。 - オブジェクト指向設計の5つの原則「SOLID」を解説
TechTargetは、「オブジェクト指向設計の原則『SOLID』」に関する記事を公開した。本稿ではSOLIDの入門編として、この開発体系を具現化する5つの原則、各原則が推奨するプラクティス、そしてこの考え方が重要である理由について説明する。 - 多数の開発チームを管理できる「アジャイルリリーストレイン」の価値とは
複数の開発チームがコードに取り組むと、統合とデプロイメントが複雑になる。こうした複雑さを摩擦なく取り除くのにはアジャイルリリーストレイン(ART)が役立つツールになる可能性がある。本稿ではこのARTについて解説する。