コレオグラフィChoreography

中央の制御ポイントに依存するのではなく、システムの各コンポーネントが、ビジネス トランザクションのワークフローに関する意思決定プロセスに参加するようにします。Have each component of the system participate in the decision-making process about the workflow of a business transaction, instead of relying on a central point of control.

コンテキストと問題Context and problem

マイクロサービス アーキテクチャでは、クラウドベースのアプリケーションを複数の小さなサービスに分割し、それらのサービスが連携して、ビジネス トランザクションをエンドツーエンドで処理することがよくあります。In microservices architecture, it’s often the case that a cloud-based application is divided into several small services that work together to process a business transaction end-to-end. サービス間の結合を弱くするために、各サービスは 1 つのビジネス操作を担当します。To lower coupling between services, each service is responsible for a single business operation. 開発の高速化、コード ベースの縮小、スケーラビリティなどの利点があります。Some benefits include faster development, smaller code base, and scalability. ただし、効率的でスケーラブルなワークフローの設計は難しく、複雑なサービス間通信が必要になることがよくあります。However, designing an efficient and scalable workflow is a challenge and often requires complex interservice communication.

サービスは、明確に定義された API を使用して、互いに通信します。The services communicate with each other by using well-defined APIs. 1 つのビジネス操作の結果として、すべてのサービス間で複数のポイントツーポイント呼び出しが発生する可能性もあります。Even a single business operation can result in multiple point-to-point calls among all services. 通信の一般的なパターンは、オーケストレーターとして機能する集中化されたサービスを使用するというものです。A common pattern for communication is to use a centralized service that acts as the orchestrator. すべての受信要求を確認し、それぞれのサービスに操作を委任します。It acknowledges all incoming requests and delegates operations to the respective services. その過程で、ビジネス トランザクション全体のワークフローも管理します。In doing so, it also manages the workflow of the entire business transaction. 各サービスは操作を完了するだけであり、ワークフロー全体は認識しません。Each service just completes an operation and is not aware of the overall workflow.

オーケストレーター パターンはサービス間のポイントツーポイント通信を削減しますが、オーケストレーターと、ビジネス トランザクションの処理に参加する他のサービスとの間の密接な結合に起因するいくつかの欠点があります。The orchestrator pattern reduces point-to-point communication between services but has some drawbacks because of the tight coupling between the orchestrator and other services that participate in processing of the business transaction. タスクを順番に実行するために、オーケストレーターには各サービスの責任に関する一定の領域知識が必要です。To execute tasks in a sequence, the orchestrator needs to have some domain knowledge about the responsibilities of those services. サービスを追加または削除する場合、既存のロジックが壊れるため、通信パスの一部を再設計する必要があります。If you want to add or remove services, existing logic will break, and you'll need to rewire portions of the communication path. 適切に設計されたオーケストレーターを使用すれば、ワークフローの構成や、サービスの追加または削除が簡単に行えますが、そのような実装は複雑であり、メンテナンスが困難です。While you can configure the workflow, add or remove services easily with a well-designed orchestrator, such an implementation is complex and hard to maintain.

中央のオーケストレーターを使用して要求を処理する

解決策Solution

中央のオーケストレーターに依存するのではなく、業務をいつどのように処理するかを各サービスで決定できます。Let each service decide when and how a business operation is processed, instead of depending on a central orchestrator.

コレオグラフィを実装する 1 つの方法は、非同期メッセージング パターンを使用してビジネス操作をコーディネートすることです。One way to implement choreography is to use the asynchronous messaging pattern to coordinate the business operations.

コレオグラファーを使用して要求を処理する

クライアント要求はメッセージ キューにメッセージを発行します。A client request publishes messages to a message queue. メッセージが到着すると、そのメッセージに関心のあるサブスクライバーまたはサービスにプッシュされます。As messages arrive, they are pushed to subscribers, or services, interested in that message. サブスクライブされた各サービスは、メッセージの指示どおりに操作を実行し、メッセージ キューへの応答で操作の成功または失敗を報告します。Each subscribed service does their operation as indicated by the message and responds to the message queue with success or failure of the operation. 成功した場合、必要な場合に別のサービスがワークフローを継続できるよう、サービスは同じキューまたは異なるメッセージ キューにメッセージをプッシュ バックできます。In case of success, the service can push a message back to the same queue or a different message queue so that another service can continue the workflow if needed. 操作が失敗した場合、メッセージ バスはその操作を再試行できます。If an operation fails, the message bus can retry that operation.

このようにして、サービスは、オーケストレーターに依存したり、サービス間で直接通信したりすることなく、サービス間でワークフローの "振り付け" を行います。This way, the services choreograph the workflow among themselves without depending on an orchestrator or having direct communication between them.

ポイントツーポイント通信がないため、このパターンはサービス間の結合を弱めるのに役立ちます。Because there isn't point-to-point communication, this pattern helps reduce coupling between services. また、すべてのトランザクションを扱う必要がある場合に、オーケストレーターに起因するパフォーマンスのボトルネックを解消できます。Also, it can remove the performance bottleneck caused by the orchestrator when it has to deal with all transactions.

このパターンを使用する状況When to use this pattern

新しいサービスを頻繁に更新、削除、または追加することが予想される場合、コレオグラフィ パターンを使用します。Use the choreography pattern if you expect to update, remove, or add new services frequently. より少ない労力で、既存のサービスを極力中断せずに、アプリ全体を変更できます。The entire app can be modified with lesser effort and minimal disruption to existing services.

中央のオーケストレーターでパフォーマンスのボトルネックが発生する場合は、このパターンを検討してください。Consider this pattern if you experience performance bottlenecks in the central orchestrator.

このパターンはサーバーレス アーキテクチャの自然なモデルであり、すべてのサービスを短期型またはイベント駆動型にすることができます。This pattern is a natural model for the serverless architecture where all services can be short lived, or event driven. イベントのためにサービスを生成し、サービスのタスクを実行させて、タスクが終了したらサービスを削除することができます。Services can spin up because of an event, do their task, and are removed when the task is finished.

問題と注意事項Issues and considerations

オーケストレーターを非集中化すると、ワークフローの管理中に問題が発生する可能性があります。Decentralizing the orchestrator can cause issues while managing the workflow.

サービスがビジネス操作を完了できない場合に、その障害からの復旧が難しい場合があります。If a service fails to complete a business operation, it can be difficult to recover from that failure. 1 つの方法は、イベントを発生させることによってサービスが障害を示すようにする、というものです。One way is to have the service indicate failure by firing an event. 障害を示すイベントをサブスクライブしている別のサービスは、補償トランザクションを適用するなどの必要なアクションを実行して、要求内の成功した操作を元に戻します。Another service subscribes to those failed events takes necessary actions such as applying compensating transactions to undo successful operations in a request. 障害が発生したサービスは、障害のイベントを発生させることにも失敗する場合があります。The failed service might also fail to fire an event for the failure. その場合は、再試行やタイムアウトのメカニズムを使用して、その操作を失敗と認識することを検討してください。In that case, consider using a retry and, or time out mechanism to recognize that operation as a failure. 例については、「例」のセクションを参照してください。For an example, see the Example section.

独立したビジネス操作を並列で処理する場合、ワークフローを実装するのは簡単です。It's simple to implement a workflow when you want to process independent business operations in parallel. 単一のメッセージ バスを使用できます。You can use a single message bus. しかし、コレオグラフィを順番に実行する必要がある場合、ワークフローは複雑になる可能性があります。However, the workflow can become complicated when choreography needs to occur in a sequence. たとえば、サービス C は、サービス A とサービス B が正常に操作を完了した後でないと操作を開始できません。For instance, Service C can start its operation only after Service A and Service B have completed their operations with success. 1 つのアプローチは、必要な順序でメッセージを取得する複数のメッセージ バスを設けることです。One approach is to have multiple message buses that get messages in the required order. 詳細については、「」のセクションを参照してください。For more information, see the Example section.

サービスの数が急速に増加する場合、コレオグラフィ パターンは困難になります。The choreography pattern becomes a challenge if the number of services grow rapidly. 独立した移動する要素の多さを考慮すると、サービス間のワークフローは複雑になる傾向があります。Given the high number of independent moving parts, the workflow between services tends to get complex. また、分散トレースは困難になります。Also, distributed tracing becomes difficult.

オーケストレーターはワークフローの回復性を集中的に管理し、単一障害点になる可能性があります。The orchestrator centrally manages the resiliency of the workflow and it can become a single point of failure. これに対して、コレオグラフィの場合は、役割がすべてのサービス間で分散されるため、回復性の堅牢さが低下します。On the other hand, for choreography, the role is distributed between all services and resiliency becomes less robust.

各サービスは、その操作の回復性だけでなくワークフローにも責任を負います。Each service isn't only responsible for the resiliency of its operation but also the workflow. この責任はサービスにとって負担になり、実装が難しいことがあります。This responsibility can be burdensome for the service and hard to implement. 各サービスは必要に応じて、要求が正常に終了するように、一時的、非一時的、およびタイムアウトのエラーを再試行する必要があります。Each service must retry transient, nontransient, and time-out failures, so that the request terminates gracefully, if needed. またサービスは、他のサービスがそれに応じた対応ができるよう、操作の成功または失敗をできるだけ伝達する必要があります。Also, the service must be diligent about communicating the success or failure of the operation so that other services can act accordingly.

Example

この例は、Drone Delivery アプリでのコレオグラフィ パターンを示しています。This example shows the choreography pattern with the Drone Delivery app. クライアントが集荷を要求すると、アプリはドローンを割り当ててクライアントに通知します。When a client requests a pickup, the app assigns a drone and notifies the client.

GitHub ロゴ このパターンの例は GitHub にあります。GitHub logo An example of this pattern is available on GitHub.

自動生成されたマップ説明のクローズアップ

1 つのクライアント ビジネス トランザクションには、荷物の作成または更新、荷物を配達するドローンの割り当て、配達ステータスの確認という 3 つの異なるビジネス操作が必要です。A single client business transaction requires three distinct business operations: creating or updating a package, assigning a drone to deliver the package, and checking the delivery status. これらの操作は 3 つのマイクロサービスによって実行されます。Package、Drone Scheduler、および Delivery の各サービスです。Those operations are performed by three microservices: Package, Drone Scheduler, and Delivery services. 中央のオーケストレーターの代わりに、サービス同士がメッセージングを使用して連携し、サービス間で要求を調整します。Instead of a central orchestrator, the services use messaging to collaborate and coordinate the request among themselves.

デザインDesign

ビジネス トランザクションは複数のホップを通じて順番に処理されます。The business transaction is processed in a sequence through multiple hops. 各ホップにメッセージ バスとそれぞれのビジネス サービスがあります。Each hop has a message bus and the respective business service.

クライアントが HTTP エンドポイントを通じて配達要求を送信すると、Ingestion サービスがそれを受信し、操作イベントを発生させてメッセージ バスに送信します。When a client sends a delivery request through an HTTP endpoint, the Ingestion service receives it, raises an operation event, and sends it to a message bus. バスはサブスクライブしているビジネス サービスを呼び出して、POST 要求でイベントを送信します。The bus invokes the subscribed business service and sends the event in a POST request. イベントを受信すると、ビジネス サービスが操作を完了して成功または失敗を返す場合と、要求がタイムアウトになる場合があります。成功した場合、サービスは Ok ステータス コードでバスに応答し、新しい操作イベントを発生させて、次のホップのメッセージ バスに送信します。On receiving the event, the business service can complete the operation with success, failure, or the request can time out. If successful, the service responds to the bus with the Ok status code, raises a new operation event, and sends it to the message bus of the next hop. 失敗またはタイムアウトの場合、サービスは、元の POST 要求を送信したメッセージ バスに BadRequest コードを送信することによって、失敗を報告します。In case of a failure or time-out, the service reports failure by sending the BadRequest code to the message bus that sent the original POST request. メッセージ バスは再試行ポリシーに基づいて操作を再試行します。The message bus retries the operation based on a retry policy. その期限を過ぎると、メッセージ バスは失敗した操作にフラグを付け、要求全体はそれ以上処理されなくなります。After that period expires, message bus flags the failed operation and further processing of the entire request stops.

このワークフローは、要求全体が処理されるまで続きます。This workflow continues until the entire request has been processed.

この設計では、複数のメッセージ バスを使用してビジネス トランザクション全体を処理します。The design uses multiple message buses to process the entire business transaction. Microsoft Azure Event Grid はメッセージング サービスを提供します。Microsoft Azure Event Grid provides the messaging service. アプリは、同じポッドに 2 つのコンテナーがある Azure Kubernetes Service (AKS) クラスターにデプロイされます。The app is deployed in an Azure Kubernetes Service (AKS) cluster with two containers in the same pod. 1 つのコンテナーは Event Grid と対話するアンバサダーを実行し、もう 1 つはビジネス サービスを実行します。One container runs the ambassador that interacts with Event Grid while the other runs a business service. 同じポッドに 2 つのコンテナーを置くアプローチにより、パフォーマンスとスケーラビリティが向上します。The approach with two containers in the same pod improves performance and scalability. アンバサダーとビジネス サービスは同じネットワークを共有するので、短い待機時間と高いスループットが実現します。The ambassador and the business service share the same network allowing for low latency and high throughput.

処理を重複させることがある再試行操作のカスケードを避けるために、ビジネス サービスの代わりに Event Grid のみが操作を再試行します。To avoid cascading retry operations that might lead to multiple efforts, only Event Grid retries an operation instead of the business service. 配信不能キュー (DLQ) にメッセージを送信することによって、失敗した要求にフラグを付けます。It flags a failed request by sending a messaging to a dead letter queue (DLQ).

再試行操作によってリソースが重複しないことを保証するために、ビジネス サービスの結果は常に同じです。The business services are idempotent to make sure retry operations don’t result in duplicate resources. たとえば、Package サービスは upsert 操作を使用してデータをデータ ストアに追加します。For example, the Package service uses upsert operations to add data to the data store.

例では、すべてのサービスおよび Event Grid ホップにわたって呼び出しを相互に関連させるためのカスタム ソリューションを実装します。The example implements a custom solution to correlate calls across all services and Event Grid hops.

すべてのビジネス サービス間のコレオグラフィ パターンを示すコード例を次に示します。Here’s a code example that shows the choreography pattern between all business services. これは、Drone Delivery アプリのトランザクションのワークフローを示しています。It shows the workflow of the Drone Delivery app transactions. 簡潔にするため、例外処理とログ記録のコードは省略しています。Code for exception handling and logging have been removed for brevity.

[HttpPost]
[Route("/api/[controller]/operation")]
[ProducesResponseType(typeof(void), 200)]
[ProducesResponseType(typeof(void), 400)]
[ProducesResponseType(typeof(void), 500)]

public async Task<IActionResult> Post([FromBody] EventGridEvent[] events)
{

   if (events == null)
   {
       return BadRequest("No Event for Choreography");
   }

   foreach(var e in events)
   {

        List<EventGridEvent> listEvents = new List<EventGridEvent>();
        e.Topic = eventRepository.GetTopic();
        e.EventTime = DateTime.Now;
        switch (e.EventType)
        {
            case Operations.ChoreographyOperation.ScheduleDelivery:
            {
                var packageGen = await packageServiceCaller.UpsertPackageAsync(delivery.PackageInfo).ConfigureAwait(false);
                if (packageGen is null)
                {
                    //BadRequest allows the event to be reprocessed by Event Grid
                    return BadRequest("Package creation failed.");
                }

                //we set the event type to the next choreography step
                e.EventType = Operations.ChoreographyOperation.CreatePackage;
                listEvents.Add(e);
                await eventRepository.SendEventAsync(listEvents);
                return Ok("Created Package Completed");
            }
            case Operations.ChoreographyOperation.CreatePackage:
            {
                var droneId = await droneSchedulerServiceCaller.GetDroneIdAsync(delivery).ConfigureAwait(false);
                if (droneId is null)
                {
                    //BadRequest allows the event to be reprocessed by Event Grid
                    return BadRequest("could not get a drone id");
                }
                e.Subject = droneId;
                e.EventType = Operations.ChoreographyOperation.GetDrone;
                listEvents.Add(e);
                await eventRepository.SendEventAsync(listEvents);
                return Ok("Drone Completed");
            }
            case Operations.ChoreographyOperation.GetDrone:
            {
                var deliverySchedule = await deliveryServiceCaller.ScheduleDeliveryAsync(delivery, e.Subject);
                return Ok("Delivery Completed");
            }
            return BadRequest();
    }
}

コレオグラフィの設計では、これらのパターンを検討してください。Consider these patterns in your design for choreography.