アプリケーションを分析して分解の境界を識別する
アプリケーションをマイクロサービス アーキテクチャに移行するには、Fabrikam は、現在のアプリケーションを評価し、各マイクロサービスのスコープと境界を決定する必要があります。 この評価のために、"ドメイン駆動設計" (DDD) フレームワークを使用しようとしています。 これをアプリケーションに適用する方法を見ていきましょう。
注意
この記事では、ドメインの完全かつ包括的な分析については説明しません。 主要なポイントを示すために、あえて簡単な例を取り上げています。 DDD の詳細については、このモジュールの最後のまとめにある「詳細情報」セクションを参照してください。
ドメイン駆動設計とは
DDD は、2005 年に『ドメイン駆動設計: ソフトウェアの核心にある複雑さに立ち向かう』という書籍で Erik Evans 氏によって最初に紹介されたシステム設計のアプローチです。 このアプローチには、3 つの主な要素があります。
- コア ドメインとドメイン ロジックに焦点を当てる。
- ドメインのモデルで設計を構造化する。
- テクニカル チームとビジネス パートナーの間で反復的なコラボレーションを促進し、システムを絶えず改善する。
DDD では、適切に設計されたマイクロサービスのセットを作り出すためのフレームワークが提供されます。 これには、"戦略的" と "戦術的" の 2 つの異なるフェーズがあります。 戦略的 DDD では、システムの大規模な構造を定義します。 戦略的 DDD は、アーキテクチャがビジネス機能に焦点を当てた状態を維持するのに役立ちます。 戦術的 DDD は、ドメイン モデルの作成に使うことのできる一連の設計パターンを提供します。 これらのパターンには、エンティティ、集約、ドメイン サービスがあります。 このような戦術的パターンを使うと、疎結合で高凝集のマイクロサービスを設計できます。
DDD の戦略的フェーズでは、ビジネス用ドメインを策定し、ドメイン モデルの境界付けられたコンテキストを定義します。 戦術的 DDD は、ドメイン モデルをより正確に定義する場合に使用します。 戦術的パターンは、1 つの境界付けられたコンテキスト内で適用されます。 マイクロサービス アーキテクチャで興味深いのは、エンティティと集約のパターンです。 これらのパターンを適用すると、アプリケーション内のサービスで自然な境界を識別できます。 一般的な原則として、マイクロサービスは集約より小さくなく、境界付けられたコンテキストより大きくないようにする必要があります。
このプロセスは、大きく次の 4 つの手順に分けることができます。
- ビジネス用ドメインを分析して、アプリケーションの機能的な要件を理解します。 この手順により、正式でないドメインの記述が生成されるため、これをより正式なドメイン モデルのセットに作り替えることができます。
- ドメインの境界付けられたコンテキストを定義します。 境界付けられた各コンテキストには、大規模なアプリケーションの特定のサブドメインを表すドメイン モデルを含めます。
- 境界付けられたコンテキスト内で、戦術的 DDD パターンを適用して、エンティティ、集約、およびドメイン サービスを定義します。
- 前の手順の結果を使って、アプリケーション内のマイクロサービスを識別します。
これらの各手順での作業を詳しく見ていきましょう。
ビジネス用ドメインを分析する
DDD では、最初にビジネス用ドメインをモデリングして、ドメイン モデルを作成します。 ドメイン モデルは、ビジネス用ドメインの抽象化モデルです。 ドメイン ナレッジが抽出および整理され、開発者とドメイン エキスパートに共通の言語が提供されます。
最初に、すべてのビジネス機能とその関係をマッピングします。 この分析は、ドメインの専門家、ソフトウェア アーキテクト、その他の利害関係者が関与する共同作業です。 特定の形式を使う必要はありません。 図をスケッチするか、ホワイトボードに描きます。
図を描画したら、個別のサブドメインの識別を開始できます。 密接に関連している機能はどれか、 ビジネスの中核となる機能はどれか、付帯的なサービスを提供する機能はどれか、 依存関係グラフとは何かを確認します。 この最初のフェーズでは、テクノロジまたは実装の詳細を気にすることはありません。 ただし、アプリケーションを外部システム (CRM、支払い処理、請求システムなど) と統合する必要のある場所については注意する必要があります。
境界付けられたコンテキストを定義する
ドメイン モデルには、世界中に存在する実際のもの (ユーザー、ドローン、荷物など) の表現が含まれます。 しかしながら、同じものについてシステム全体で同じ表現を使わなければならないというわけではありません。
たとえば、ドローンの修理と予測分析を処理するサブシステムでは、ドローンの多くの物理的特性を表す必要があります。 この特性には、メンテナンス履歴、飛行距離、年数、モデル番号、性能の詳細などが含まれます。 しかし、配送をスケジュールするときは、それらについて考慮しません。 スケジューリング サブシステムで把握する必要があるのは、ドローンが使用可能かどうかと、集配の予定時刻 (ETA) のみです。
これらのサブシステムの両方に対して 1 つのモデルを作成しようとすると、その作業は必要以上に複雑になります。 また、すべての変更について、別のサブシステムで作業する複数のチームが納得する必要があるため、時間の経過と共にモデルの進化が困難になります。 多くの場合、同じ現実世界のエンティティ (ここではドローン) を 2 つの異なるコンテキストで表す個別のモデルを設計することをお勧めします。 各モデルには、その特定のコンテキスト内で関連する機能と属性だけを含めます。
このアプローチでは、境界付けられたコンテキストという DDD の概念が役に立ちます。 境界付けられたコンテキストは、特定のドメイン モデルが適用されるドメイン内にある境界に過ぎません。 前述の図では、さまざまな機能が 1 つのドメイン モデルを共有するかどうかに応じて、機能をグループ化できます。
エンティティ、集計、およびサービスを定義する
戦術的 DDD は、ドメイン モデルをより正確に定義する場合に使用します。 戦術的パターンは、1 つの境界付けられたコンテキスト内で適用されます。 マイクロサービス アーキテクチャで興味深いのは、エンティティと集約のパターンです。 これらのパターンを適用すると、アプリケーション内のサービスで自然な境界を識別できます。 一般的な原則として、マイクロサービスは集約より小さくなく、境界付けられたコンテキストより大きくないようにする必要があります。
考慮すべき戦術的 DDD パターンがいくつかあります。
- エンティティ:エンティティは、時間の経過に関係なく保持される一意の ID を持つオブジェクトです。 たとえば、銀行取引アプリケーションでは、顧客と口座がエンティティです。
- 値オブジェクト:値オブジェクトは ID を持ちません。 これは、自身の属性の値によって定義され、不変です。 値オブジェクトの一般的な例として、色、日付と時刻、通貨の値があります。
- 集約:集約では、1 つ以上のエンティティを囲む一貫性の境界を定義します。 集約の目的は、トランザクションのインバリアントをモデル化することです。 現実世界のものには複雑な関係があります。 たとえば、顧客が注文を作成し、注文には商品が含まれ、商品には供給業者が存在する、などの関係です。 アプリケーションがいくつかの関連オブジェクトを変更する場合は、どのように一貫性を保証するのでしょうか。 また、インバリアントを追跡して実行するにはどうすればよいでしょうか。
- ドメイン サービスとアプリケーション サービス:DDD の用語では、サービスとは、状態を保持せずに何らかのロジックを実装するオブジェクトです。 Evans 氏は、ドメイン ロジックをカプセル化するドメイン サービスと、技術的な機能を提供するアプリケーション サービスを区別しています。 通常、アプリケーション サービスには、ユーザー認証や SMS メッセージの送信などの技術的な機能が含まれます。 ドメイン サービスは、多くの場合、複数のエンティティにまたがる動作のモデル化に使用されます。
- ドメイン イベント:ドメイン イベントを使うと、何かが発生したときにシステムの他の部分に通知できます。 その名前が示すように、ドメイン イベントとは、ドメイン内の何かを意味します。 たとえば、"レコードがテーブルに挿入された" はドメイン イベントではありません。 "配送がキャンセルされた" はドメイン イベントです。 ドメイン イベントはマイクロサービス アーキテクチャに特に関連します。 マイクロサービスは分散型であり、データ ストアを共有しないので、ドメイン イベントはマイクロサービスが互いに連携するための方法を提供します。
Fabrikam の開発チームは、自社のシステムで、次のエンティティを識別しました。
- 配送
- Package
- ドローン
- Account
- 確認
- Notification
- タグ
最初の 4 つのエンティティ (配送、荷物、ドローン、アカウント) はすべて、トランザクションの一貫性の境界を表す集約です。 確認と通知は配送の子エンティティです。 タグは荷物の子エンティティです。
この設計の値オブジェクトには、Location、ETA、PackageWeight、PackageSize があります。
2 つのドメイン イベントがあります。
- ドローンの飛行中、ドローン エンティティにより、ドローンの場所と状態 (飛行中、着陸など) を示す DroneStatus イベントが送信されます。
- 配送エンティティにより、配送のステージが変わるたびに DeliveryTracking イベントが送信されます。 これらのイベントには、DeliveryCreated、DeliveryRescheduled、DeliveryHeadedToDropoff、DeliveryCompleted があります。
これらのイベントは、ドメイン モデル内で意味のある内容を示しています。 ドメインに関する情報を示し、特定のプログラミング言語コンストラクトには関連付けられません。
開発チームは、これまでに記述されたどのエンティティにも当てはまらないもう 1 つの機能分野を識別しました。 システムの一部では、配送のスケジューリングまたは更新に関連するすべての手順を調整する必要があります。 開発チームは、設計に 2 つのドメインサービスを追加しました。 Scheduler では手順を調整します。 スーパーバイザーは、何らかの手順が失敗したりタイムアウトしたりしていないかを検出するために、各手順の状態を監視します。
マイクロサービスを識別する
これで、ドメイン モデルからアプリケーション設計に移る準備ができました。 ドメイン モデルからマイクロサービスを導き出すために使うことができるアプローチを次に示します。
- 境界付けられたコンテキストから始めます。 一般に、マイクロサービスの機能は、複数の境界付けられたコンテキストにまたがることはできません。 定義上、境界付けられたコンテキストは特定のドメイン モデルの境界をマークします。 1 つのマイクロサービスにさまざまなドメイン モデルが混在している場合、それはドメイン分析を修正する必要があるかもしれないという兆候です。
- 次に、ドメイン モデルの集計を確認します。 集計はマイクロサービスの適切な候補になることがよくあります。 適切に設計された集約は、以下のような適切に設計されたマイクロサービスの特徴を数多く示します。
- 集約は、データ アクセスやメッセージングなどの技術的な問題ではなく、ビジネス要件から導き出されます。
- 集約には、機能の高凝集が必要です。
- 集計は永続性の境界です。
- 集計同士は疎に結合されている必要があります。
- ドメイン サービスもマイクロサービスに適した候補です。 ドメイン サービスは、複数の集計に対するステートレスな操作です。 典型的な例は、複数のマイクロサービスを含むワークフローです。 後で、ドローン配送アプリケーションのドメイン サービスの例を確認します。
- 最後に、非機能要件について検討します。 チームの規模、データの型、テクノロジ、スケーラビリティの要件、可用性の要件、セキュリティの要件などの要因を確認します。 これらの要因が原因で、1 つのマイクロサービスをより小さい 2 つ (またはさらに多数) のサービスにさらに分割したり、その逆を行っていくつかのマイクロサービスを 1 つに結合したりする場合があります。
実際的であること、そしてドメイン駆動設計は反復プロセスであることを忘れないようにすることが重要です。 判断に迷うときは、より粒度の粗いマイクロサービスから始めるようにします。 1 つのマイクロサービスをより小さい 2 つのサービスに分割する方が、いくつかの既存のマイクロサービスにわたって機能をリファクタリングするより簡単です。
ドローン アプリケーションにドメイン駆動設計を適用する
Fabrikam のアプリケーションでは、これらすべてのサービスが既存のモノリシック アプリケーションに存在します。 Fabrikam は、アプリケーションをマイクロサービスに分解できる場所を特定した後、パッケージ サービスに関する作業を開始します。
現在、パッケージ サービスには、専任の開発チームが存在していますが、スケーラビリティに関連するパフォーマンスの問題が発生しているため、アプリケーションの分解の起点とするのに最適な候補となっています。