次の方法で共有


マイクロサービス間のイベント ベースの通信の実装 (統合イベント)

ヒント

このコンテンツは、.NET Docs で入手できる、またはオフラインで読み取ることができる無料のダウンロード可能な PDF として入手できる、コンテナー化された .NET アプリケーションの電子ブックである .NET マイクロサービス アーキテクチャからの抜粋です。

コンテナー化された .NET アプリケーションの .NET マイクロサービス アーキテクチャの電子ブックの表紙サムネイル。

前述のように、 イベント ベースの通信を使用する場合、 マイクロサービス は、ビジネス エンティティを更新するときなど、注目すべきことが発生したときにイベントを発行します。 他のマイクロサービスは、これらのイベントをサブスクライブします。 マイクロサービスは、イベントを受信すると、独自のビジネス エンティティを更新できるため、より多くのイベントが発行される可能性があります。 これが最終的な整合性の概念の本質です。 この パブリッシュ/サブスクライブ システムは、通常、イベント バスの実装を使用して実行されます。 イベント バスは、イベントのサブスクライブとサブスクライブ解除、およびイベントの発行に必要な API とのインターフェイスとして設計できます。 また、非同期通信とパブリッシュ/サブスクライブ モデルをサポートするメッセージング キューやサービス バスなど、プロセス間またはメッセージング通信に基づく 1 つ以上の実装を含めることもできます。

イベントを使用すると、複数のサービスにまたがるビジネス トランザクションを実装できるため、それらのサービス間の最終的な一貫性が得られます。 最終的に一貫性のあるトランザクションは、一連の分散アクションで構成されます。 各アクションで、マイクロサービスはビジネス エンティティを更新し、次のアクションをトリガーするイベントを発行します。 トランザクションは基盤となる永続性やイベント バスにはまたがらないため、べき等性を考慮する必要があります。 下の図 6-18 は、イベント バスを介して発行された PriceUpdated イベントを示しています。そのため、価格更新は Basket やその他のマイクロサービスに伝達されます。

非同期イベントドリブン通信を行うイベントバスの図。

図 6-18 イベントバスに基づいたイベント駆動型通信

このセクションでは、図 6-18 に示すように、汎用イベント バス インターフェイスを使用して、この種の .NET との通信を実装する方法について説明します。 複数の潜在的な実装があり、それぞれが RabbitMQ、Azure Service Bus、その他のサードパーティのオープンソースまたは商用サービス バスなどの異なるテクノロジまたはインフラストラクチャを使用しています。

実稼働システムにメッセージ ブローカーとサービス バスを使用する

アーキテクチャのセクションで説明したように、抽象イベント バスを実装するための複数のメッセージング テクノロジから選択できます。 しかし、これらのテクノロジは異なるレベルにあります。 たとえば、メッセージング ブローカー トランスポートである RabbitMQ は、Azure Service Bus、NServiceBus、MassTransit、Brighter などの商用製品よりも低いレベルです。 これらの製品のほとんどは、RabbitMQ または Azure Service Bus の上で動作します。 製品の選択は、機能の数と、アプリケーションに必要なすぐに使用できるスケーラビリティによって異なります。

eShopOnContainers サンプルのように、開発環境用のイベント バスの概念実証のみを実装するには、 RabbitMQ をコンテナーとして実行する上での単純な実装で十分な場合があります。 ただし、高いスケーラビリティを必要とするミッション クリティカルな運用システムの場合は、 Azure Service Bus を評価して使用することをお勧めします。

分散開発を容易にする実行時間の長いプロセスに対して Sagas のような高度な抽象化と豊富な機能が必要な場合は、 NServiceBusMassTransitBrighter などの他の商用およびオープンソースのサービス バスを評価する価値があります。 この場合、通常、使用する抽象化と API は、独自の抽象化 ( eShopOnContainers で提供される単純なイベント バスの抽象化など) ではなく、これらの高レベルのサービス バスによって提供されるものです。 その場合は、NServiceBus (特定のソフトウェアによって実装される追加の派生サンプル) を 使用して、フォークされた eShopOnContainers を調査できます。

もちろん、RabbitMQ や Docker などの下位レベルのテクノロジの上に独自の Service Bus 機能を常に構築できますが、"ホイールの再発明" に必要な作業は、カスタムエンタープライズ アプリケーションにはコストがかかりすぎる可能性があります。

繰り返しになりますが、eShopOnContainers サンプルで紹介されているサンプル イベント バスの抽象化と実装は、概念実証としてのみ使用することを目的としています。 現在のセクションで説明したように、非同期およびイベントドリブン通信を使用することを決定したら、運用環境のニーズに最適な Service Bus 製品を選択する必要があります。

統合イベント

統合イベントは、複数のマイクロサービスまたは外部システム間でドメインの状態を同期するために使用されます。 この機能は、マイクロサービスの外部で統合イベントを発行することによって行われます。 イベントが複数の受信側マイクロサービス (統合イベントにサブスクライブされている数のマイクロサービス) に発行されると、各受信側マイクロサービスの適切なイベント ハンドラーがイベントを処理します。

統合イベントは、次の例のように、基本的にはデータ保持クラスです。

public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
    public int ProductId { get; private set; }
    public decimal NewPrice { get; private set; }
    public decimal OldPrice { get; private set; }

    public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice,
        decimal oldPrice)
    {
        ProductId = productId;
        NewPrice = newPrice;
        OldPrice = oldPrice;
    }
}

統合イベントは、各マイクロサービスのアプリケーション レベルで定義できるため、他のマイクロサービスから切り離され、ViewModel がサーバーとクライアントでどのように定義されるかに相当します。 推奨されないのは、複数のマイクロサービス間で共通の統合イベント ライブラリを共有することです。これを行うと、これらのマイクロサービスと単一のイベント定義データ ライブラリが結合されます。 複数のマイクロサービス間で共通ドメイン モデルを共有したくないのと同じ理由で、これを行いたくありません。マイクロサービスは完全に自律的である必要があります。 詳細については、 イベントに格納するデータの量に関するこのブログ記事を参照してください。 行き過ぎないように注意してください。この他のブログ記事では、データ不足メッセージが引き起こす可能性がある問題について説明しています。 イベントの設計は、コンシューマーのニーズに対して "正しい" ようにすることを目指す必要があります。

マイクロサービス間で共有する必要があるライブラリの種類はごくわずかです。 1 つは、eShopOnContainers のように、 Event Bus クライアント API のような最終的なアプリケーション ブロックであるライブラリです。 もう 1 つは、JSON シリアライザーなどの NuGet コンポーネントとして共有できるツールを構成するライブラリです。

イベント バス

イベント バスを使用すると、図 6-19 に示すように、コンポーネントが互いを明示的に認識する必要なく、マイクロサービス間の発行/サブスクライブ スタイルの通信が可能になります。

基本的なパブリッシュ/サブスクライブ パターンを示す図。

図 6-19 イベント バスでの発行/サブスクライブの基礎

上の図は、マイクロサービス A が Event Bus に発行されることを示しています。これは、パブリッシャーがサブスクライバーを知る必要なく、サブスクライブしているマイクロサービス B と C に分散されます。 イベント バスは、オブザーバー パターンとパブリッシュ/サブスクライブ パターンに関連しています。

オブザーバー パターン

オブザーバー パターンでは、プライマリ オブジェクト (Observable と呼ばれます) が、関連する情報 (イベント) を使用して他の関心のあるオブジェクト (オブザーバーと呼ばれます) に通知します。

発行/購読 (Pub/Sub) パターン

発行/サブスクライブ パターンの目的はオブザーバー パターンと同じです。特定のイベントが発生したときに他のサービスに通知する必要があります。 ただし、オブザーバー パターンと Pub/Sub パターンには重要な違いがあります。 オブザーバーパターンでは、ブロードキャストはObservableからオブザーバーに直接実行されるため、互いに『知っている』ことになります。 ただし、Pub/Sub パターンを使用する場合は、ブローカー、メッセージ ブローカー、またはイベント バスと呼ばれる 3 つ目のコンポーネントがあり、パブリッシャーとサブスクライバーの両方で認識されます。 したがって、Pub/Sub パターンを使用する場合、前述のイベント バスまたはメッセージ ブローカーにより、パブリッシャーとサブスクライバーが正確に切り離されます。

仲介者またはイベントバス

パブリッシャーとサブスクライバーの間で匿名性を実現する方法 簡単な方法は、仲介者がすべてのコミュニケーションを管理できるようにすることです。 イベント バスはそのような仲介者の 1 つです。

イベント バスは、通常、次の 2 つの部分で構成されます。

  • 抽象化またはインターフェイス。

  • 1 つ以上の実装。

図 6-19 では、アプリケーションの観点から見ると、イベント バスが Pub/Sub チャネルに過ぎないことがわかります。 この非同期通信を実装する方法はさまざまです。 環境の要件 (運用環境と開発環境など) に応じて、複数の実装を使用してそれらを交換できます。

図 6-20 では、RabbitMQ、Azure Service Bus、または別のイベント/メッセージ ブローカーなどのインフラストラクチャ メッセージング テクノロジに基づいて、複数の実装を持つイベント バスの抽象化を確認できます。

イベント バス抽象化レイヤーの追加を示す図。

図 6- 20 イベント バスの複数の実装

RabbitMQ、Azure Service Bus などの複数のテクノロジを使用して実装できるように、インターフェイスを介してイベント バスを定義することをお勧めします。 ただし、前述のように、独自の抽象化 (イベント バス インターフェイス) の使用は、抽象化でサポートされている基本的なイベント バス機能が必要な場合にのみ適しています。 より豊富なサービス バス機能が必要な場合は、独自の抽象化ではなく、推奨される商用サービス バスによって提供される API と抽象化を使用する必要があります。

イベント バス インターフェイスの定義

まず、イベント バス インターフェイス用の実装コードと、探索目的で可能な実装から始めましょう。 インターフェイスは、次のインターフェイスのように、汎用的で単純である必要があります。

public interface IEventBus
{
    void Publish(IntegrationEvent @event);

    void Subscribe<T, TH>()
        where T : IntegrationEvent
        where TH : IIntegrationEventHandler<T>;

    void SubscribeDynamic<TH>(string eventName)
        where TH : IDynamicIntegrationEventHandler;

    void UnsubscribeDynamic<TH>(string eventName)
        where TH : IDynamicIntegrationEventHandler;

    void Unsubscribe<T, TH>()
        where TH : IIntegrationEventHandler<T>
        where T : IntegrationEvent;
}

Publishメソッドは簡単です。 イベント バスは、そのイベントに渡された統合イベントを、そのイベントにサブスクライブしている任意のマイクロサービスまたは外部アプリケーションにブロードキャストします。 このメソッドは、イベントを発行するマイクロサービスによって使用されます。

イベントを受信するマイクロサービスでは、 Subscribe メソッド (引数に応じていくつかの実装を使用できます) が使用されます。 このメソッドには 2 つの引数があります。 1つ目は、購読するための統合イベント (IntegrationEvent) です。 2 番目の引数は、 IIntegrationEventHandler<T>という名前の統合イベント ハンドラー (またはコールバック メソッド) で、受信側マイクロサービスがその統合イベント メッセージを取得したときに実行されます。

その他のリソース

運用環境に対応したメッセージング ソリューションの一部: