ヒント
このコンテンツは、.NET Docs で入手できる、またはオフラインで読み取ることができる無料のダウンロード可能な PDF として入手できる、コンテナー化された .NET アプリケーションの電子ブックである .NET マイクロサービス アーキテクチャからの抜粋です。
ドメイン駆動型設計 (DDD) では、ユース ケースに関連するビジネスの現実に基づくモデリングが推奨されます。 アプリケーションの構築のコンテキストでは、DDD は問題をドメインとして説明します。 独立した問題領域を有界コンテキスト (各境界コンテキストはマイクロサービスに関連付ける) として説明し、これらの問題について話す共通言語を強調します。 また、豊富なモデルを持つドメイン エンティティ ( 貧血ドメイン モデルなし)、値オブジェクト、集計、集計ルート (またはルート エンティティ) ルールなど、多くの技術的な概念とパターンを提案して、内部実装をサポートします。 このセクションでは、これらの内部パターンの設計と実装について説明します。
これらの DDD 技術規則とパターンは、DDD アプローチを実装するための急な学習曲線を持つ障害として認識される場合があります。 しかし、重要な部分はパターン自体ではなく、ビジネス上の問題に合わせてコードを整理し、同じビジネス用語 (ユビキタス言語) を使用することです。 さらに、DDD アプローチは、重要なビジネス ルールを持つ複雑なマイクロサービスを実装する場合にのみ適用する必要があります。 CRUD サービスのようなよりシンプルな責任は、より簡単な方法で管理できます。
マイクロサービスを設計および定義する際に、境界を描画する場所が重要なタスクです。 DDD パターンは、ドメインの複雑さを理解するのに役立ちます。 各境界コンテキストのドメイン モデルでは、ドメインをモデル化するエンティティ、値オブジェクト、集計を特定して定義します。 コンテキストを定義する境界内に含まれるドメイン モデルを構築および調整します。 これはマイクロサービスの形式で明示的です。 これらの境界内のコンポーネントはマイクロサービスになりますが、BC マイクロサービスまたはビジネス マイクロサービスは、いくつかの物理サービスで構成される場合があります。 DDD は境界に関するものであり、マイクロサービスについても説明します。
マイクロサービス コンテキストの境界を比較的小さくする
境界コンテキスト間の境界を配置する場所を決定すると、2 つの競合する目標のバランスが取られます。 最初に、可能な限り最小のマイクロサービスを作成する必要がありますが、これはメイン ドライバーではありません。まとまりが必要なものの周りに境界を作成する必要があります。 次に、マイクロサービス間で冗長なコミュニケーションが発生しないようにする必要があります。 これらの目標は互いに矛盾する可能性があります。 新しい境界付きコンテキストを分離する試行が追加されるたびに通信境界が急速に拡大するのを見るまで、システムをできるだけ多くの小さなマイクロサービスに分解することで、バランスを取る必要があります。 凝集は、1 つの境界付けられたコンテキスト内のキーです。
これは、クラスを実装するときの 不適切な親密なコードの臭いに 似ています。 2 つのマイクロサービスが互いに多くの共同作業を行う必要がある場合は、おそらく同じマイクロサービスである必要があります。
この側面を見るもう 1 つの方法は、自律性です。 マイクロサービスが要求に直接サービスを提供するために別のサービスに依存する必要がある場合、それは本当に自律的ではありません。
DDD マイクロサービスのレイヤー
ビジネスと技術的な複雑さが大きいほとんどのエンタープライズ アプリケーションは、複数のレイヤーによって定義されます。 レイヤーは論理成果物であり、サービスのデプロイには関連しません。 これらは、開発者がコードの複雑さを管理するのに役立ちます。 異なるレイヤー (ドメイン モデル レイヤーとプレゼンテーション レイヤーなど) には異なる種類があり、それらの型間の変換が必要な場合があります。
たとえば、エンティティをデータベースから読み込むことができます。 その後、その情報の一部、または他のエンティティからの追加データを含む情報の集計を、REST Web API を介してクライアント UI に送信できます。 ここでのポイントは、ドメイン エンティティがドメイン モデル レイヤー内に含まれており、プレゼンテーション レイヤーのように、ドメイン エンティティが属していない他の領域に伝達されないことです。
さらに、集約ルート (ルート エンティティ) によって制御される、常に有効なエンティティ ( ドメイン モデル レイヤーでの検証の設計 に関するセクションを参照) が必要です。 そのため、UI レベルでは一部のデータがまだ検証されない可能性があるため、エンティティをクライアント ビューにバインドしないでください。 この理由は、ViewModel の目的です。 ViewModel は、プレゼンテーション レイヤーのニーズ専用のデータ モデルです。 ドメイン エンティティは ViewModel に直接属していません。 代わりに、ViewModel とドメイン エンティティの間で変換し、その逆を行う必要があります。
複雑さに取り組む場合は、集約ルートによって制御されるドメイン モデルを持つことが重要です。これにより、そのエンティティのグループ (集計) に関連するすべてのインバリアントとルールが、集約ルートである単一のエントリ ポイントまたはゲートを介して実行されます。
図 7-5 は、eShopOnContainers アプリケーションで階層化されたデザインがどのように実装されるかを示しています。

図 7-5. eShopOnContainers の注文マイクロサービスの DDD レイヤー
注文などの DDD マイクロサービス内の 3 つのレイヤー。 各レイヤーは VS プロジェクトです。アプリケーション レイヤーは Ordering.API、Domain レイヤーは Ordering.Domain、Infrastructure レイヤーは Ordering.Infrastructure です。 各レイヤーが特定の他のレイヤーとのみ通信するようにシステムを設計する必要があります。 ライブラリ間で設定されている依存関係を明確に識別できるため、レイヤーが異なるクラス ライブラリとして実装されている場合は、このアプローチを適用する方が簡単な場合があります。 たとえば、ドメイン モデル レイヤーが他のレイヤーに依存しないようにする必要があります (ドメイン モデル クラスは、Plain Old Class Objects または POCO クラスである必要があります)。 図 7-6 に示すように、 Ordering.Domain レイヤー ライブラリには .NET ライブラリまたは NuGet パッケージにのみ依存関係がありますが、データ ライブラリや永続化ライブラリなどの他のカスタム ライブラリには依存しません。

図 7-6 ライブラリとして実装されたレイヤーを使用すると、レイヤー間の依存関係をより適切に制御できます
ドメイン モデル レイヤー
Eric Evans の優れた書籍 「ドメイン駆動設計 」では、ドメイン モデル レイヤーとアプリケーション レイヤーについて次のように述べています。
ドメイン モデル レイヤー: ビジネスの概念、ビジネス状況に関する情報、ビジネス ルールを表す役割を担います。 ビジネス状況を反映する状態は、格納の技術的な詳細がインフラストラクチャに委任されている場合でも、ここで制御および使用されます。 このレイヤーは、ビジネス ソフトウェアの中核です。
ドメイン モデル レイヤーは、ビジネスが表現される場所です。 .NET でマイクロサービス ドメイン モデル レイヤーを実装すると、そのレイヤーは、データと動作 (ロジックを含むメソッド) をキャプチャするドメイン エンティティを含むクラス ライブラリとしてコード化されます。
永続化の無視とインフラストラクチャの無視の原則に従って、このレイヤーはデータ永続化の詳細を完全に無視する必要があります。 これらの永続化タスクは、インフラストラクチャ レイヤーによって実行する必要があります。 そのため、このレイヤーはインフラストラクチャに直接依存しないようにする必要があります。つまり、重要なルールは、ドメイン モデル エンティティ クラスが POC である必要があることを意味します。
ドメイン エンティティは、Entity Framework や NHibernate などのデータ アクセス インフラストラクチャ フレームワークに対する直接的な依存関係 (基底クラスからの派生など) を持つべきではありません。 理想的には、ドメイン エンティティは、インフラストラクチャ フレームワークで定義されている型から派生したり、実装したりしないでください。
Entity Framework Core のような最新の ORM フレームワークのほとんどは、このアプローチを許可しているため、ドメイン モデル クラスはインフラストラクチャに結合されません。 ただし、Azure Service Fabric のアクターや Reliable Collection など、特定の NoSQL データベースとフレームワークを使用する場合、POCO エンティティを持つことは必ずしも可能であるとは限りません。
ドメイン モデルの永続化の無視の原則に従することが重要な場合でも、永続化の問題を無視しないでください。 物理データ モデルと、それがエンティティ オブジェクト モデルにどのようにマップされるかを理解することは、依然として重要です。 そうしないと、不可能なデザインを作成できます。
また、この側面は、リレーショナル データベース用に設計されたモデルを取得し、それを NoSQL またはドキュメント指向データベースに直接移動できることを意味するものではありません。 一部のエンティティ モデルでは、モデルが適合する場合がありますが、通常は適合しません。 ストレージ テクノロジと ORM テクノロジの両方に基づいて、エンティティ モデルが準拠する必要がある制約がまだあります。
アプリケーション レイヤー
アプリケーション層に進み、Eric Evans の書籍 「ドメイン駆動設計」を再び引用できます。
アプリケーション レイヤー: ソフトウェアが実行するジョブを定義し、表現型ドメイン オブジェクトに問題を解決するよう指示します。 このレイヤーが担当するタスクは、ビジネスにとって意味があり、他のシステムのアプリケーション レイヤーとの対話に必要です。 この層は薄く保たれます。 ビジネス ルールやナレッジは含まれませんが、タスクを調整し、次のレイヤーのドメイン オブジェクトのコラボレーションに作業を委任するだけです。 ビジネス状況を反映する状態はありませんが、ユーザーまたはプログラムのタスクの進行状況を反映する状態を持つことができます。
.NET のマイクロサービスのアプリケーション レイヤーは、通常、ASP.NET Core Web API プロジェクトとしてコーディングされます。 このプロジェクトは、マイクロサービスの相互作用、リモート ネットワーク アクセス、UI またはクライアント アプリから使用される外部 Web API を実装します。 CQRS アプローチを使用する場合のクエリ、マイクロサービスによって受け入れられるコマンド、マイクロサービス間のイベント ドリブン通信 (統合イベント) も含まれます。 アプリケーション 層を表す ASP.NET Core Web API には、ビジネス ルールまたはドメインナレッジ (特にトランザクションまたは更新のドメイン ルール) を含めてはなりません。これらはドメイン モデル クラス ライブラリによって所有されている必要があります。 アプリケーション レイヤーは、タスクの調整のみを行う必要があり、ドメインの状態 (ドメイン モデル) を保持または定義することはできません。 ビジネス ルールの実行がドメイン モデル クラス自体 (集約ルートとドメイン エンティティ) に委任され、最終的にそれらのドメイン エンティティ内のデータが更新されます。
基本的に、アプリケーション ロジックでは、特定のフロントエンドに依存するすべてのユース ケースを実装します。 たとえば、Web API サービスに関連する実装などです。
目標は、ドメイン モデル レイヤー内のドメイン ロジック、その不変性、データ モデル、および関連するビジネス ルールが、プレゼンテーション層とアプリケーション レイヤーから完全に独立している必要があるということです。 何より、ドメイン モデル レイヤーはインフラストラクチャ フレームワークに直接依存してはなりません。
インフラストラクチャ レイヤー
インフラストラクチャ レイヤーは、ドメイン エンティティ (メモリ内) に最初に保持されたデータをデータベースまたは別の永続的なストアに保持する方法です。 たとえば、Entity Framework Core コードを使用して、DBContext を使用してリレーショナル データベースにデータを保持するリポジトリ パターン クラスを実装します。
前述の 永続化の無視 と インフラストラクチャの無視 の原則に従って、インフラストラクチャ レイヤーはドメイン モデル レイヤーを "汚染" してはなりません。 ドメイン モデル エンティティ クラスは、フレームワークへのハード依存関係を取らずに、データ (EF またはその他のフレームワーク) を保持するために使用するインフラストラクチャに依存しないようにする必要があります。 ドメイン モデル レイヤー クラス ライブラリには、ドメイン コードのみを含める必要があり、ソフトウェアの中核を実装する POCO エンティティ クラスのみが必要であり、インフラストラクチャ テクノロジから完全に切り離されます。
したがって、図 7-7 に示すように、レイヤーまたはクラス ライブラリとプロジェクトは最終的にドメイン モデル レイヤー (ライブラリ) に依存する必要があります。その逆ではありません。

図 7-7 DDD のレイヤー間の依存関係
DDD サービスの依存関係、アプリケーションレイヤーはドメインとインフラストラクチャに依存し、インフラストラクチャはドメインに依存しますが、ドメインはどのレイヤーにも依存しません。 このレイヤーの設計は、マイクロサービスごとに独立している必要があります。 前述のように、DDD パターンに従って最も複雑なマイクロサービスを実装しながら、より単純なデータドリブン マイクロサービス (単一レイヤーの単純な CRUD) を簡単な方法で実装できます。
その他のリソース
DevIQ。 永続性無視の原則
https://deviq.com/persistence-ignorance/オレン・エイニ。 インフラストラクチャの無視
https://ayende.com/blog/3137/infrastructure-ignorance天使ロペス. Domain-Driven 設計における階層構造
https://ajlopez.wordpress.com/2008/09/12/layered-architecture-in-domain-driven-design/
.NET