マイクロサービス間でイベント ベースの通信を実装する (統合イベント)Implementing event-based communication between microservices (integration events)

前述のように、イベント ベースの通信を使う場合、ビジネス エンティティの更新などの重要なできごとが発生すると、マイクロサービスがイベントを発行します。As described earlier, when you use event-based communication, a microservice publishes an event when something notable happens, such as when it updates a business entity. その他のマイクロサービスは、これらのイベントをサブスクライブします。Other microservices subscribe to those events. マイクロサービスは、イベントを受信するときにそれ自体のビジネス エンティティを更新する場合があり、それによってさらにイベントが発行される可能性があります。When a microservice receives an event, it can update its own business entities, which might lead to more events being published. これは、最終的な整合性の概念の本質です。This is the essence of the eventual consistency concept. この発行/サブスクライブ システムは、通常はイベント バスの実装を使って実行されます。This publish/subscribe system is usually performed by using an implementation of an event bus. イベント バスは、イベントのサブスクライブおよびサブスクライブ解除とイベントの発行に必要な API を含むインターフェイスとして設計することができます。The event bus can be designed as an interface with the API needed to subscribe and unsubscribe to events and to publish events. また、イベント バスは、任意のプロセス間通信またはメッセージング通信に基づく 1 つ以上の実装を使うことができます。たとえば、非同期通信と発行/サブスクライブ モデルをサポートするメッセージング キューまたはサービス バスです。It can also have one or more implementations based on any inter-process or messaging communication, such as a messaging queue or a service bus that supports asynchronous communication and a publish/subscribe model.

イベントを使用して、複数のサービスにまたがるビジネス トランザクションを実装できます。これにより、サービス間の最終的な整合性が実現されます。You can use events to implement business transactions that span multiple services, which give you eventual consistency between those services. 最終的に整合性のあるトランザクションは、一連の分散アクションで構成されます。An eventually consistent transaction consists of a series of distributed actions. 各アクションにおいて、マイクロサービスはビジネス エンティティを更新し、次のアクションをトリガーするイベントを発行します。At each action, the microservice updates a business entity and publishes an event that triggers the next action. 次の図 6-18 は、PriceUpdated イベントがイベント バスを介して発行され、価格の更新はバスケットやその他のマイクロサービスに反映されることを示しています。Figure 6-18 below, shows a PriceUpdated event published through an event bus, so the price update is propagated to the Basket and other microservices.

イベント バスを使用した非同期のイベント ドリブン通信の図。

図 6-18Figure 6-18. イベント バスに基づくイベントドリブン通信Event-driven communication based on an event bus

このセクションでは、図 6-18 に示すように、ジェネリックなイベント バスのインターフェイスを使って、この種類の通信を .NET で実装する方法について説明します。This section describes how you can implement this type of communication with .NET by using a generic event bus interface, as shown in Figure 6-18. さまざまなテクノロジやインフラストラクチャ (RabbitMQ、Azure Service Bus、またはその他のサード パーティ製のオープン ソースまたは商用のサービス バスなど) を使った、複数の実装が可能です。There are multiple potential implementations, each using a different technology or infrastructure such as RabbitMQ, Azure Service Bus, or any other third-party open-source or commercial service bus.

運用システムでのメッセージ ブローカーとサービス バスの使用Using message brokers and services buses for production systems

アーキテクチャのセクションで説明したように、抽象イベント バスを実装するためのメッセージング テクノロジには、複数の選択肢があります。As noted in the architecture section, you can choose from multiple messaging technologies for implementing your abstract event bus. ただし、これらのテクノロジは動作するレベルが異なります。But these technologies are at different levels. たとえば、メッセージング ブローカーのトランスポートである RabbitMQ は、Azure Service Bus、NServiceBus、MassTransit、Brighter などの商用の製品よりも低いレベルで動作します。For instance, RabbitMQ, a messaging broker transport, is at a lower level than commercial products like Azure Service Bus, NServiceBus, MassTransit, or Brighter. これらの製品のほとんどは、RabbitMQ または Azure Service Bus の上部で動作させることができます。Most of these products can work on top of either RabbitMQ or Azure Service Bus. 製品の選択は、アプリケーションに必要な機能の数や、すぐに利用できるスケーラビリティの量によって異なります。Your choice of product depends on how many features and how much out-of-the-box scalability you need for your application.

開発環境でイベント バスの概念実証を実装するだけの場合は、eShopOnContainers サンプルのように、コンテナーとして実行される RabbitMQ の上部の単純な実装で十分です。For implementing just an event bus proof-of-concept for your development environment, as in the eShopOnContainers sample, a simple implementation on top of RabbitMQ running as a container might be enough. ただし、高いスケーラビリティが求められるミッションクリティカルな運用システムでは、Azure Service Bus を評価して使うことをお勧めします。But for mission-critical and production systems that need high scalability, you might want to evaluate and use Azure Service Bus.

分散開発を容易にする実行時間の長いプロセスのために、Sagas のような高レベルの抽象化と豊富な機能が必要な場合は、NServiceBus、MassTransit、Brighter などのその他の商用およびオープン ソース サービス バスをお勧めします。If you require high-level abstractions and richer features like Sagas for long-running processes that make distributed development easier, other commercial and open-source service buses like NServiceBus, MassTransit, and Brighter are worth evaluating. この場合は通常、独自の抽象化ではなく、これらの高レベルのサービス バスによって提供される抽象化と API を直接使います (eShopOnContainers で提供される単純なイベント バスの抽象化のように)。In this case, the abstractions and API to use would usually be directly the ones provided by those high-level service buses instead of your own abstractions (like the simple event bus abstractions provided at eShopOnContainers). さらに、NServiceBus を使う eShopOnContainers の分岐 (Particular Software によって実装される追加の派生サンプル) を調査することもできます。For that matter, you can research the forked eShopOnContainers using NServiceBus (additional derived sample implemented by Particular Software).

もちろん、常に RabbitMQ や Docker のような下位レベルのテクノロジの上に独自のサービス バス機能を構築することもできますが、カスタム エンタープライズ アプリケーションの場合、"車輪の再発明" に必要な作業はコストがかかりすぎる可能性があります。Of course, you could always build your own service bus features on top of lower-level technologies like RabbitMQ and Docker, but the work needed to "reinvent the wheel" might be too costly for a custom enterprise application.

繰り返しになりますが、eShopOnContainers サンプルで紹介されたサンプルのイベント バスの抽象化と実装は、概念実装としてのみ使用されます。To reiterate: the sample event bus abstractions and implementation showcased in the eShopOnContainers sample are intended to be used only as a proof of concept. 現在のセクションで説明されているように、非同期とイベント起動型の通信が必要であると判断したら、運用環境でのニーズに最適なサービス バス製品を選ぶ必要がります。Once you have decided that you want to have asynchronous and event-driven communication, as explained in the current section, you should choose the service bus product that best fits your needs for production.

統合イベントIntegration events

統合イベントは、ドメインを複数のマイクロサービスまたは外部システムにわたって同期するために使われます。Integration events are used for bringing domain state in sync across multiple microservices or external systems. この機能は、統合イベントをマイクロサービスの外部に発行することによって実現されます。This functionality is done by publishing integration events outside the microservice. 複数の受信側マイクロサービス (統合イベントをサブスクライブしているすべてのマイクロサービス) にイベントが発行されると、各受信側マイクロサービスの適切なイベント ハンドラーがイベントを処理します。When an event is published to multiple receiver microservices (to as many microservices as are subscribed to the integration event), the appropriate event handler in each receiver microservice handles the event.

統合イベントは、次の例のように、基本的にはデータを保持するクラスです。An integration event is basically a data-holding class, as in the following example:

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;
    }
}

統合イベントは各マイクロサービスのアプリケーション レベルで定義できるため、他のマイクロサービスから切り離されます。これは、ViewModels をサーバーやクライアントで定義する方法に相当します。The integration events can be defined at the application level of each microservice, so they are decoupled from other microservices, in a way comparable to how ViewModels are defined in the server and client. 共通の統合イベント ライブラリを複数のマイクロサービス間で共有することはお勧めできません。これらのマイクロサービスを、単一のイベント定義データ ライブラリで結合することになるからです。What is not recommended is sharing a common integration events library across multiple microservices; doing that would be coupling those microservices with a single event definition data library. これを回避する理由は、共通のドメイン モデルを複数のマイクロサービス間で共有しない理由と同じです。つまり、マイクロサービスは完全に自律している必要があります。You do not want to do that for the same reasons that you do not want to share a common domain model across multiple microservices: microservices must be completely autonomous.

マイクロサービス間で共有する必要があるライブラリは数種類のみです。There are only a few kinds of libraries you should share across microservices. 1 つは、eShopOnContainers の場合ように、イベント バスのクライアント API のような、最後のアプリケーション ブロックとなるライブラリです。One is libraries that are final application blocks, like the Event Bus client API, as in eShopOnContainers. もう 1 つは、JSON シリアライザーのように、NuGet コンポーネントとしても共有できるツールを構成するライブラリです。Another is libraries that constitute tools that could also be shared as NuGet components, like JSON serializers.

イベント バスThe event bus

イベント バスを使うと、図 6-19 に示すように、コンポーネントが互いを明示的に認識せずに、マイクロサービス間の発行/サブスクライブ スタイルの通信が可能になります。An event bus allows publish/subscribe-style communication between microservices without requiring the components to explicitly be aware of each other, as shown in Figure 6-19.

基本的な発行/サブスクライブ パターンを示す図。

図 6-19Figure 6-19. イベント バスでの発行/サブスクライブの基礎Publish/subscribe basics with an event bus

上の図は、マイクロサービス A でイベント バスに発行し、これにより、発行者がサブスクライバーを知らなくても、マイクロサービス B と C をサブスクライブするように分散されることを示しています。The above diagram shows that microservice A publishes to Event Bus, which distributes to subscribing microservices B and C, without the publisher needing to know the subscribers. イベント バスは、オブザーバー パターンと発行-サブスクライブ パターンに関連しています。The event bus is related to the Observer pattern and the publish-subscribe pattern.

オブザーバー パターンObserver pattern

オブザーバー パターンでは、プライマリ オブジェクト (オブザーバブルと呼ばれます) が登録されている他のオブジェクト (オブザーバーと呼ばれます) に関連情報 (イベント) を通知します。In the Observer pattern, your primary object (known as the Observable) notifies other interested objects (known as Observers) with relevant information (events).

発行/サブスクライブ (Pub/Sub) パターンPublish/Subscribe (Pub/Sub) pattern

発行/サブスクライブ パターンの目的はオブザーバー パターンと同じです。特定のイベントが発生したことを他のサービスに通知する必要があります。The purpose of the Publish/Subscribe pattern is the same as the Observer pattern: you want to notify other services when certain events take place. ただし、オブザーバー パターンと Pub/Sub パターンの間には重要な違いがあります。But there is an important difference between the Observer and Pub/Sub patterns. オブザーバー パターンでは、オブザーバブルからオブザーバーに直接ブロードキャストされるため、これらは互いを "認識" しています。In the observer pattern, the broadcast is performed directly from the observable to the observers, so they "know" each other. しかし、Pub/Sub パターンを使う場合は、ブローカー、メッセージ ブローカーまたはイベント バスと呼ばれる、発行者とサブスクライバーの両方から認識される第 3 のコンポーネントが存在します。But when using a Pub/Sub pattern, there is a third component, called broker, or message broker or event bus, which is known by both the publisher and subscriber. そのため、Pub/Sub パターンを使うと、前述したイベント バス (またはメッセージ ブローカー) によって、発行者とサブスクライバーが厳密に切り離されます。Therefore, when using the Pub/Sub pattern the publisher and the subscribers are precisely decoupled thanks to the mentioned event bus or message broker.

仲介者またはイベント バスThe middleman or event bus

発行者とサブスクライバーの間の匿名性は、どのように実現すればよいのでしょうか。How do you achieve anonymity between publisher and subscriber? 簡単な方法は、仲介者にすべての通信を処理させることです。An easy way is let a middleman take care of all the communication. イベント バスは、このような仲介者の 1 つです。An event bus is one such middleman.

イベント バスは通常 2 つの部分で構成されます。An event bus is typically composed of two parts:

  • 抽象化またはインターフェイス。The abstraction or interface.

  • 1 つ以上の実装。One or more implementations.

図 6-19 では、イベント バスが、アプリケーションの観点からは、Pub/Sub のチャネルにすぎないことがわかります。In Figure 6-19 you can see how, from an application point of view, the event bus is nothing more than a Pub/Sub channel. この非同期通信は、さまざまな方法で実装できます。The way you implement this asynchronous communication can vary. 環境の要件 (たとえば、運用環境と開発環境) に応じて実装を切り替えるために、複数の実装を使うことができます。It can have multiple implementations so that you can swap between them, depending on the environment requirements (for example, production versus development environments).

図 6-20 では、インフラストラクチャ メッセージング テクノロジ (RabbitMQ、Azure Service Bus、あるいはその他のイベントまたはメッセージ ブローカーなど) に基づく複数の実装での、イベント バスの抽象化を確認できます。In Figure 6-20, you can see an abstraction of an event bus with multiple implementations based on infrastructure messaging technologies like RabbitMQ, Azure Service Bus, or another event/message broker.

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

図 6-20Figure 6- 20. イベント バスの複数の実装Multiple implementations of an event bus

RabbitMQ Azure Service バスなどのいくつかのテクノロジで実装できるように、インターフェイスから定義されたイベント バスを用意することをお勧めします。It's good to have the event bus defined through an interface so it can be implemented with several technologies, like RabbitMQ Azure Service bus or others. ただし、前述したように、独自の抽象化 (イベントバスのインターフェイス) を使用することが適しているのは、その抽象化によってサポートされる基本的なイベント バスの機能が必要な場合のみです。However, and as mentioned previously, using your own abstractions (the event bus interface) is good only if you need basic event bus features supported by your abstractions. より豊富なサービス バスの機能が必要な場合は、独自の抽象化ではなく、好みの商用サービス バスによって提供される API と抽象化を使うことをお勧めします。If you need richer service bus features, you should probably use the API and abstractions provided by your preferred commercial service bus instead of your own abstractions.

イベント バスのインターフェイスの定義Defining an event bus interface

イベント バスのインターフェイスの実装コードの一部と、探索のための実装例から始めましょう。Let's start with some implementation code for the event bus interface and possible implementations for exploration purposes. 以下のように、インターフェイスは汎用的で単純である必要があります。The interface should be generic and straightforward, as in the following interface.

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 メソッドは単純です。The Publish method is straightforward. イベント バスは、渡される統合イベントを、そのイベントをサブスクライブするすべてのマイクロサービス、または外部アプリケーションに、ブロードキャストします。The event bus will broadcast the integration event passed to it to any microservice, or even an external application, subscribed to that event. このメソッドは、イベントを発行するマイクロサービスによって使われます。This method is used by the microservice that is publishing the event.

Subscribe メソッド (引数に応じていくつかの実装を使えます) は、イベントを受信するマイクロサービスによって使われます。The Subscribe methods (you can have several implementations depending on the arguments) are used by the microservices that want to receive events. このメソッドには、2 つの引数があります。This method has two arguments. 1 つ目は、サブスクライブする統合イベントです (IntegrationEvent)。The first is the integration event to subscribe to (IntegrationEvent). 2 番目の引数は、受信側マイクロサービスが統合イベントのメッセージを取得すると実行される、統合イベントのハンドラー (またはコールバック メソッド) です (IIntegrationEventHandler<T>)。The second argument is the integration event handler (or callback method), named IIntegrationEventHandler<T>, to be executed when the receiver microservice gets that integration event message.

その他の技術情報Additional resources

実稼働可能なメッセージング ソリューションの一部:Some production-ready messaging solutions: