エリックエバンスのドメイン駆動設計の「第6章 ドメインオブジェクトのライフサイクル」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第6章 ドメインオブジェクトのライフサイクル」のまとめです。

概要

永続化を伴うようなライフサイクルの寿命が長いドメインオブジェクトを管理することは、以下のような課題を解決する必要があるので、難しくなります。

  1. ライフサイクルを通じて、不変条件を維持すること。

  2. ドメインオブジェクト間の複雑な相互依存関係を解決すること。

  3. ライフサイクルを管理するのが複雑でも、モデルが浸食されないようにすること。

ドメイン駆動設計では、3つのパターン(集約、ファクトリ、リポジトリ)を通して、これらの課題に対応しています。

  • 集約は課題の1.と2.に対応。

  • ファクトリは課題の3.に対応。

  • リポジトリは課題の3.に対応。

不変条件とは?

課題1.で説明されている不変条件とは、 何かの業務を行う時は維持すべき重要なビジネルルールが常に守られていること。です。

まずは、業務視点で守るべきビジネスルールを明確化した上で、システムとしての対応は

  • 一つの集約内で適用される不変条件はトランザクション完了時に強制する。

  • 複数の集約にまたがるルールは何らかの更新の仕組みを通じて、一定時間内で維持される。

ということになります。

※具体的な例は、集約のところで説明します。

集約

目的

複雑なドメインモデルにおいて、ライフサイクルの遷移に伴う一貫性を保証することは難しい状況になりえます。

その状況化で、重要なビジネスルールが守られることを保証するために、ドメインとして境界を定義して、境界内で重要なビジネスルールが守られることを保証します。

その境界の一つ一つが集約という単位になります。

定義

  • エンティティと値オブジェクトを集約の中にまとめ、各集約の周囲に境界を定義すること。

  • 各集約に対してルートとなるエンティティを1つ選んで、境界の内部に存在するオブジェクトへのアクセスは、ルートを経由して制御すること。

  • 外部オブジェクトが参照を保持できるのはルートのみ。

  • 集約で区切られた範囲で不変条件を維持すること。

具体例

DDD本で説明されていた例になりますが、『ダメな集約例』と『イケテル集約例』です。

f:id:mmm-mao:20150526083819j:plain f:id:mmm-mao:20150526083816j:plain

『ダメな集約例』のどこがダメかというと、以下の流れで注文明細が操作されたケースです。 集約の単位が購入注文明細になっているので、フェーズ2の同タイミングで操作された場合に、ビジネルルール(承認限度額>=注文明細の総額)が破られてしまいます。

f:id:mmm-mao:20150526084826j:plain

そのため、今回のビジネルルールを守るためには、購入注文と購入注文明細を1つの集約として捉える必要があります。

ファクトリ

目的

集約の生成に関しては、以下の課題があります。

  • 複雑な集約の生成は、生成される集約の責務に合わない。

  • 仮に生成の責務を集約が担ってしまうと、責務が明確になりずらくなる。

  • 集約のクライアントに直接生成させると、クライアントの本来の責務が不明瞭になり、生成される集約のカプセル化も違反することになる。

これらの課題を解決するためにファクトリという考え方を用います。

定義

  • ライフサイクルの始まりに対応して、複雑な集約を生成する。

  • 集約を生成するのが複雑になったり、内部構造をさらけ出しすぎていたりする場合、複雑な内部構造をカプセル化する。

  • 集約ごとにファクトリを一つ用意する。

リポジトリ

目的

ライフサイクルの初期以降後に、何かの業務イベントを実行するためには、既に存在するドメインオブジェクトが必要になります。 また、実行後は、ドメインオブジェクトを永続化する必要があります。

ドメイン駆動設計の目標は、技術ではなく、ドメインに焦点を合わせることなので、上のような具体的な手段をカプセル化するために、リポジトリという考え方を用います。

定義

  • 永続化されたオブジェクトにアクセスする手段を提供する。

  • クライアントに対して、永続化されたオブジェクトを取得し、ライフサイクルを管理するためのユビキタス言語に関するシンプルな操作を提供する。

  • ドメインの設計から、永続化技術などから分離する。

  • 集約ごとにリポジトリを一つ用意する。

具体例

ドメインの設計から永続化技術などを分離するために、ドメイン層としてはリポジトリのインターフェースだけを提供して、インフラストラクチャで具体的な技術手段を用いた操作を提供する。という責務にしています。

また、永続化する部分に関しても、よくsaveメソッドだけ提供されている例を見かけますが、僕が普段開発する場合は、ユビキタス言語に関するシンプルな操作(登録や退会)を提供しています。

f:id:mmm-mao:20150526081842j:plain

まとめ

集約を区切るスコープによって、不変条件の維持すべき範囲が示され、ライフサイクルの遷移における複雑さはファクトリとリポジトリによってカプセル化されます。

以上が「第6章 ドメインオブジェクトのライフサイクル」のまとめです。