このドキュメントでは、ドメイン駆動設計 (DDD)、CQRS、およびイベント ソーシングを実装するプロジェクトである foodtruacker について詳しく説明します。 ASP.NET Core を使用し、複雑なビジネス ドメインの保守性の向上に重点を置いています。このプロジェクトでは、説明のために簡略化した架空のビジネス ケースを使用します。この詳細な説明では、動機、機能、実装の詳細、関連テクノロジーについて説明します。
foodtruacker - DDD、CQRS、イベント ソーシングの実装
このイベント駆動型プロジェクトは、原則、フレームワーク、アーキテクチャを利用しており、これらはすべて、複雑なビジネス ドメインを反映するシステムを扱う際の保守性を向上させるという考えを中心にしています。アプリケーションの Web API は Microsoft の ASP.NET Core フレームワークに基づいて構築されており、ドメイン駆動設計、CQRS およびイベント ソーシング パターンを実装しています。架空のビジネス ケースがこのプロジェクトの基礎を築き、イベント ストーミング ワークショップの結果として生まれました。
注意してください: このプロジェクトに導入された架空のビジネス ドメインは大幅に簡略化されており、関連性のあるユースケースのプロバイダーとしてのみ見るべきです。
モチベーション
かなり複雑なビジネス ドメインを持つプロジェクトで CRUD 操作と POCO オブジェクトを使用することが常に最善であるとは限らないため、私はドメイン駆動設計 (DDD) に関する私の研究と興味の実践的な実装としてこのプロジェクトを作成することにしました。ソフトウェア開発のアプローチ。
このプロジェクトに導入された架空のビジネス ケースはイベント駆動型が多いため、CQRS パターンとイベント ソーシング パターンも実装することにしました。どちらもこのプロジェクトのリサーチ中に私の注目を集めたもので、DDD とよく合います。
特徴
概要
このプロジェクトは、1 つの実行可能な Web API アプリケーションと、それぞれがクラス ライブラリを通じて提供されるいくつかの機能コンポーネントで構成されています。コードは名前空間ごとに編成されています。 Visual Studio で作成された ASP.NET Core アプリケーションでは、名前空間は既定で、プロジェクトのフォルダー構造から自動的に作成されます。このプロジェクトのフォルダー (および名前空間) 構造の概要については、次の図を参照してください。
導入
イベントストーミング
アルベルト ブランドリーニが発明した、複雑なビジネス領域を共同で探索するための柔軟なワークショップ形式。これは、組織内のビジネス フローとプロセスを迅速に改善、構想、調査、設計するための非常に軽量な方法論です。
このワークショップは、さまざまな専門知識を持つ人々のグループで構成され、色付きの付箋を使用して、関連するビジネス プロセスを協力してレイアウトします。 EventStorming ワークショップでは、適切な人が出席することと、付箋を貼るのに十分な表面積を確保することが必須です。必要な人材には通常、尋ねるべき質問を知っている人 (通常は開発者) と答えを知っている人 (ドメインの専門家、製品所有者) が含まれます。
このワークショップの目的は、参加者が互いに学び合い、誤解を明らかにして反論し、たとえばこの GitHub プロジェクトにおいて、関連するビジネス ドメインを反映するイベント ベースのソフトウェア ソリューションの開発の基礎を築くことです。
ドメイン駆動設計 (DDD)
関連するビジネス ドメインのプロセスとルールを十分に理解したドメイン モデルのプログラミングを開発の中心とするソフトウェア開発のアプローチ。 「ドメイン駆動設計」という用語は、エリック・エヴァンスが同名の著書の中で生み出した造語です。
DDD は、複雑なアプリケーションの作成を容易にすることを目的としており、次の 3 つの中心原則に焦点を当てています。
Eric Evans の本では、ドメイン駆動設計に関するいくつかの一般的な用語が定義されています。
ドメインモデル
ビジネス ドメインのプロセスとポリシーを記述し、そのドメインに関連付けられた必要なタスクを処理するために使用される抽象化システム。
ユビキタス言語
ビジネス領域の特定の要素に関する言葉や記述。再び誤解を防ぐために、チームメンバー全員が特定の用語、通常はドメインの専門家によって使用される用語を採用する必要があります。
境界付きコンテキスト
特定のドメイン モデルが定義され適用される概念的な境界。これは通常、サブシステムまたは作業分野を表します。これは主に言語的な境界であり、境界付けられた各コンテキストには独自のユビキタス言語があります。
例: 顧客管理では、ユーザーは「顧客」と呼ばれます。
Eric Evans の本では、ドメイン モデルの特定の部分をさらに区別しています。いくつか例を挙げると、
実在物
属性ではなくアイデンティティによって定義されるオブジェクト。
例: ジャケット、髪の色、または特定の瞬間に話される言語の選択に関係なく、人は常に同じ人であり続けます。
値オブジェクト
属性の値によってのみ定義されるオブジェクト。値オブジェクトは不変であり、一意の ID を持ちません。値オブジェクトは、同じ属性を持つ他の値オブジェクトに置き換えることができます。
例: 人物に焦点を合わせている場合、壊れたサングラスを、同じように見える新しいサングラスと簡単に交換できます。
集計
1 つ以上のエンティティとオプションの値オブジェクトのクラスター。単一のトランザクション単位として統合されます。 1 つのエンティティが集約のベースを形成し、それによって集約ルートとして宣言されます。連携するすべてのエンティティおよび値オブジェクトのプロパティには、この単一のベース エンティティを介してのみアクセスできます。集約は常に一貫した状態にある必要があります。オブジェクト指向プログラミングでは、これは通常、プライベート セッターと保護されたゲッターを使用して行われます。
例: 自動車販売のコンテキストでは、1 台の自動車 (エンティティ) が車両識別番号によって定義されます。この車には 4 つの車輪 (バリュー オブジェクト) が付いている可能性があり、一定期間後に交換する必要がある可能性があります。
ドメインイベント
ドメイン モデル内のアクティビティの結果として作成されるオブジェクト。このアクティビティに関連する情報を保持および転送するために使用されます。ドメイン イベントは通常、ドメインの専門家が関連すると考えるアクティビティに対して作成されます。
ヘキサゴナル アーキテクチャ (ポートとアダプター)
2005 年に Alistair Cockburn によって提案された、ソフトウェア設計で使用されるアーキテクチャ パターン。このパターンは高度な保守性を実現することを目的としており、アプリケーションを 3 つの層で記述します。各層は、インターフェイス (ポート) と実装 (アダプター) を使用して隣接する層と通信します。
このアーキテクチャ パターンの重要なルールは、依存関係は内部方向のみを指すことができるということです。内側のサークルにあるものは、外側のサークルにあるものについてまったく知ることはできません。アプリケーション層からデータベースを呼び出すなど、外部に向ける依存関係は、制御の反転 (IoC) または依存性注入 (DI) によってインスタンス化する必要があります。
MediatR (事前に構築されたメッセージング フレームワーク) を使用した CQRS
CQRS は、Command/Query Responsibility Segregation の略で、2010 年に Greg Young によって初めて説明されました。これは、Command Query Separation (CQS) 原則に基づいており、読み取り操作と書き込み操作の分離を可能にします。 CQS は次のように述べています。
CQRS が CQS よりも優れている点は、これらのコマンドとクエリがメソッドではなくモデルとして扱われることです。これらのモデルは、ある時点でオブジェクトとしてディスパッチされ、システムの別の時点で必要なそれぞれのハンドラーによって処理され、各アクションを明確に分離するためにそれぞれの応答モデルが返されます。
メディエーター パターンでは、メディエーター オブジェクトを利用して、疎結合されたコマンド/クエリとハンドラーを実装できます。オブジェクトは相互に直接通信するのではなく、メディエーターを介して通信するようになります。
MediatR フレームワークは、Jimmy Bogard によって作成されたメディエーター パターンのオープン ソース実装です。このプロジェクトでは、フレームワーク層とアプリケーション層間の通信に利用されます。また、コマンド データベースからクエリ データベースにデータを投影するためにも使用されます。
イベントソーシング
ドメイン内のデータの現在の状態だけを保存するのではなく、アプリケーションの状態のすべての変更を保存するためのアーキテクチャ設計パターン。このパターンは Greg Young によって導入され、それ以来数多くの採用が行われてきました。
このパターンは、アプリケーションの状態に対するあらゆる変化をイベント オブジェクトとしてキャプチャすることを目的としています。これらのイベント オブジェクトは、発生順に追加のみの方法で保存されます。これにより、これまでに発生した一連のイベントにわたってオブジェクトの現在の状態を再作成できるだけでなく、最終的には時間を遡って任意の時点のオブジェクトの状態を再作成することができます。
銀行口座は、イベント ソーシングの原則の良い例となります。お金の引き出しまたは預け入れのたびに、現在の残高を更新するだけでなく、お釣りの金額が記録されます。その後、一連のイベントを追跡し、毎回の引き出しまたは入金の金額に関する対応する情報を使用して、現在の残高が計算されます。
イベント ソーシングは、変更リクエストごとにドメイン モデルによってトリガーされるドメイン イベントの保存に最適であるため、ドメイン駆動設計とうまく連携します。
イベント ソーシングも CQRS から大きな恩恵を受けます。現在の状態を再作成するために、リクエストされているオブジェクトに関連するすべての記録されたイベントを調べる必要があるイベント ソーシング データベースに対してクエリを作成する代わりに、このクエリは専用のクエリ データベースに対して実行できます。このクエリ データベースは独自のイベント ハンドラーによって更新され、イベント ソーシング データベースに追加された直後に送出される同じイベントをリッスンします。これらの更新プロセスはプロジェクションと呼ばれます。
このデータベースの分離は、スケーラビリティとパフォーマンスの最適化における大きな可能性の基礎にもなります。アプリケーションの状態に関連する変更が発生した直後に、イベント ソーシング データベース クライアントから送出されるイベントをイベント ハンドラーにリッスンさせるだけで、クエリ データベースの複数のインスタンスを作成し、同期を保つことができます。データベースの種類とデータの非正規化の程度を選択し、クエリごとに最適化することにより、パフォーマンスを大幅に向上させることができます。
読み取りモデルのこの継続的な更新は、同期または非同期で実行できます。後者では、最終的な整合性が犠牲になり、わずかな時間差 (通常はミリ秒) で読み取りモデルが書き込みモデルと同期しなくなります。
共有カーネル
ドメイン層の共通ライブラリ。境界コンテキスト間で共有される、共通のドメイン駆動設計固有の基本クラス、ドメイン エンティティ、値オブジェクトなどが含まれます。
はじめる
このプロジェクトをそのまま立ち上げて実行するには、次の手順に従ってください。
前提条件
設定
ブラウザで https://localhost:5001/swagger/index.html を起動して、API の Swagger ドキュメントを表示します。
Swagger、Postman、またはその他のアプリケーションを使用して POST リクエストを https://localhost:5001/api/Administration/Register に送信し、最初の管理者アカウントを登録します。次のオブジェクトを送信します。
コンソール アプリケーション、またはアプリケーションのログ用に再構成された出力を調べます。ユーザーの登録が成功すると、EmailService によって提供される電子メール検証リンクがログに書き込まれます。この URL をコピーしてブラウザに貼り付け、Enter キーを押して登録を完了します。この不適切な電子メール サービスの実装を自由に変更したり、それに基づいて構築したりしてください ;-)
準備は完了です。次にログインします。
ブラウザで http://localhost:2113/ を起動して、EventStoreDB GUI を表示します。 [ストリーム ブラウザ] タブを開いて、保存されているすべてのイベントを表示します。
テストは以下を実行することで実行できます。
テクノロジー
このプロジェクトでは、次のテクノロジ/NuGet パッケージを利用します。
リソース / 推奨書籍
アルベルト・ブランドリーニ:
https://www.eventstorming.com
ボーン・バーノン:
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_1.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_2.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_3.pdf
アリスター・コックバーン:
https://web.archive.org/web/20180822100852/http://alistair.cockburn.us/Hexagonal+architecture
ロバート・C・マーティン (ボブおじさん):
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
セザール・デ・ラ・トーレ、ビル・ワグナー、マイク・ルソス:
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/
グレッグ・ヤング
https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
https://cqrs.wordpress.com/documents/building-event-storage/
https://msdn.microsoft.com/en-us/library/jj591559.aspx
マーティン・ファウラー:
https://www.martinfowler.com/bliki/CQRS.html
ジミー・ボガード:
https://github.com/jbogard/MediatR
https://www.youtube.com/watch?v=SUiWfhAhgQw
ドメイン駆動設計:
https://dddcommunity.org
https://thedomaindrivendesign.io
https://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/
https://dotnetcodr.com/2015/10/22/domain-driven-design-with-web-api-extensions-part-1-notifications/
六角形のアーキテクチャ:
https://fideloper.com/hexagonal-architecture
https://herbertograca.com/2017/09/14/ports-adapters-architecture/
クレジット
http://www.andreavallotti.tech/en/2018/01/event-sourcing-and-cqrs-in-c/
https://www.Exceptionnotfound.net/real-world-cqrs-es-with-asp-net-and-redis-part-1-overview/
https://buildplease.com/pages/fpc-1/
https://dotnetcoretutorials.com/2019/04/30/the-mediator-pattern-in-net-core-part-1-whats-a-mediator/
https://itnext.io/why-and-how-i-implemented-cqrs-and-mediator-patterns-in-a-microservice-b07034592b6d