ヒント
このコンテンツは、.NET Docs で入手できる、またはオフラインで読み取ることができる無料のダウンロード可能な PDF として入手できる、コンテナー化された .NET アプリケーションの電子ブックである .NET マイクロサービス アーキテクチャからの抜粋です。
課題 1: 各マイクロサービスの境界を定義する方法
マイクロサービスの境界を定義することは、おそらく誰もが遭遇する最初の課題です。 各マイクロサービスはアプリケーションの一部である必要があり、各マイクロサービスは、それが伝えるすべての利点と課題で自律的である必要があります。 しかし、それらの境界を特定するにはどうすればよいでしょうか。
まず、アプリケーションの論理ドメイン モデルと関連データに焦点を当てる必要があります。 同じアプリケーション内で、分離されたデータのアイランドとさまざまなコンテキストを特定してみてください。 各コンテキストは、異なるビジネス言語 (異なるビジネス用語) を持つことができます。 コンテキストは、個別に定義および管理する必要があります。 これらの異なるコンテキストで使用される用語とエンティティは似ているかもしれませんが、特定のコンテキストでは、あるビジネス概念が別のコンテキストで別の目的に使用され、名前が異なる場合もあることに気が付く場合があります。 たとえば、ユーザーは ID またはメンバーシップ コンテキストのユーザー、CRM コンテキストの顧客、注文コンテキストの購入者などと呼ばれます。
コンテキストごとに異なるドメインを持つ複数のアプリケーション コンテキスト間の境界を識別する方法は、各ビジネス マイクロサービスとその関連するドメイン モデルとデータの境界を正確に識別する方法です。 これらのマイクロサービス間の結合は常に最小限に抑えるようにします。 このガイドでは、後で 各マイクロサービスのドメイン モデル境界を識別する セクションで、この識別とドメイン モデルの設計について詳しく説明します。
課題 2: 複数のマイクロサービスからデータを取得するクエリを作成する方法
2 つ目の課題は、リモート クライアント アプリからマイクロサービスへのチャット通信を回避しながら、複数のマイクロサービスからデータを取得するクエリを実装する方法です。 たとえば、バスケット、カタログ、およびユーザー ID マイクロサービスが所有するユーザー情報を表示する必要があるモバイル アプリの 1 つの画面が考えられます。 もう 1 つの例は、複数のマイクロサービスに配置された多数のテーブルを含む複雑なレポートです。 適切なソリューションは、クエリの複雑さに依存します。 ただし、どのような場合でも、システムの通信の効率を向上させる場合は、情報を集計する方法が必要になります。 最も一般的なソリューションは次のとおりです。
API ゲートウェイ。 異なるデータベースを所有する複数のマイクロサービスからの単純なデータ集計の場合、推奨されるアプローチは、API ゲートウェイと呼ばれる集計マイクロサービスです。 ただし、このパターンはシステム内のチョーク ポイントになる可能性があり、マイクロサービスの自律性の原則に違反する可能性があるため、このパターンの実装には注意する必要があります。 この可能性を軽減するために、システムの垂直方向の "スライス" またはビジネス領域に焦点を当てて、細かい粒度の API ゲートウェイをそれぞれ複数作成できます。 API ゲートウェイ パターンについては、後の 「API Gateway」セクション で詳しく説明します。
GraphQL フェデレーション マイクロサービスで GraphQL が既に使用されているかどうかを考慮する 1 つのオプションは 、GraphQL フェデレーションです。 フェデレーションを使用すると、他のサービスから "サブグラフ" を定義し、それらをスタンドアロン スキーマとして機能する集計 "スーパーグラフ" に作成できます。
クエリ/読み取りテーブルを含む CQRS。 複数のマイクロサービスからデータを集計するためのもう 1 つのソリューションは、 具体化されたビュー パターンです。 この方法では、複数のマイクロサービスが所有するデータを含む読み取り専用テーブルを事前に生成します (実際のクエリが発生する前に非正規化データを準備します)。 このテーブルには、クライアント アプリのニーズに適した形式があります。
モバイル アプリの画面のようなものを考えてみましょう。 1 つのデータベースがある場合は、複数のテーブルを含む複雑な結合を実行する SQL クエリを使用して、その画面のデータをプルできます。 ただし、複数のデータベースがあり、各データベースが異なるマイクロサービスによって所有されている場合、それらのデータベースに対してクエリを実行して SQL 結合を作成することはできません。 複雑なクエリが課題となります。 CQRS アプローチを使用して要件に対処できます。クエリのみに使用される別のデータベースに非正規化テーブルを作成します。 このテーブルは、アプリケーションの画面に必要なフィールドとクエリ テーブル内の列の間に 1 対 1 のリレーションシップを持つ、複雑なクエリに必要なデータ専用に設計できます。 また、レポートの目的にも役立ちます。
この方法では、元の問題 (マイクロサービス間でのクエリと結合の方法) を解決するだけでなく、複雑な結合と比較してパフォーマンスが大幅に向上します。これは、アプリケーションがクエリ テーブルに必要とするデータが既に存在するためです。 もちろん、クエリ/読み取りテーブルでコマンドとクエリの責任分離 (CQRS) を使用すると、追加の開発作業が行われ、最終的な整合性を採用する必要があります。 ただし、 コラボレーション シナリオ (または観点に応じて競合するシナリオ) でのパフォーマンスと高いスケーラビリティに関する要件は、複数のデータベースで CQRS を適用する必要がある場所です。
中央データベースの "コールド データ" です。 リアルタイム データを必要としない複雑なレポートやクエリの場合、一般的な方法は、"ホット データ" (マイクロサービスからのトランザクション データ) を "コールド データ" としてレポートにのみ使用される大規模なデータベースにエクスポートすることです。 その中央データベース システムは、Hadoop のようなビッグ データ ベースのシステムにすることができます。Azure SQL Data Warehouse に基づくようなデータ ウェアハウス。または、レポートのみに使用される単一の SQL データベース (サイズが問題にならない場合)。
この一元化されたデータベースは、リアルタイム データを必要としないクエリとレポートにのみ使用されることに注意してください。 元の更新とトランザクションは、信頼できるソースとして、マイクロサービス データに含まれている必要があります。 データを同期する方法は、イベントドリブン通信 (次のセクションで説明) を使用するか、他のデータベース インフラストラクチャのインポート/エクスポート ツールを使用して行います。 イベントドリブン通信を使用する場合、その統合プロセスは、CQRS クエリ テーブルについて前述したようにデータを伝達する方法と似ています。
ただし、アプリケーションの設計で複雑なクエリのために複数のマイクロサービスから情報を絶えず集計する必要がある場合は、マイクロサービスを他のマイクロサービスからできるだけ分離する必要 -a 不適切な設計の症状である可能性があります。 (これは、常にコールド データ中央データベースを使用する必要があるレポート/分析を除外します)。多くの場合、この問題が発生することは、マイクロサービスをマージする理由になる可能性があります。 各マイクロサービスの進化とデプロイの自律性と、強力な依存関係、凝集、およびデータ集計のバランスを取る必要があります。
課題 3: 複数のマイクロサービス間で一貫性を実現する方法
前述のように、各マイクロサービスが所有するデータはそのマイクロサービスに対してプライベートであり、そのマイクロサービス API を使用してのみアクセスできます。 そのため、複数のマイクロサービス間で一貫性を保ちながら、エンドツーエンドのビジネス プロセスを実装する方法が課題となります。
この問題を分析するために、 eShopOnContainers 参照アプリケーションの例を見てみましょう。 Catalog マイクロサービスは、製品価格を含むすべての製品に関する情報を保持します。 Basket マイクロサービスは、ユーザーが買い物かごに追加している製品項目に関する一時的なデータを管理します。これには、バスケットに追加された時点の項目の価格が含まれます。 製品の価格がカタログで更新されると、その価格は、その同じ製品を保持するアクティブなバスケットでも更新する必要があります。さらに、システムは、特定のアイテムの価格がバスケットに追加されてから変更されたとユーザーに警告する必要があります。
このアプリケーションの架空のモノリシック バージョンでは、製品テーブルの価格が変更されると、カタログ サブシステムは ACID トランザクションを使用して、Basket テーブルの現在の価格を更新できます。
ただし、マイクロサービス ベースのアプリケーションでは、Product テーブルと Basket テーブルはそれぞれのマイクロサービスによって所有されます。 図 4-9 に示すように、マイクロサービスは、直接クエリであっても、別のマイクロサービスが所有するテーブルやストレージを独自のトランザクションに含めるべきではありません。
図 4-9. マイクロサービスが別のマイクロサービス内のテーブルに直接アクセスできない
Basket テーブルは Basket マイクロサービスによって所有されているため、Catalog マイクロサービスは Basket テーブルを直接更新しないでください。 Basket マイクロサービスを更新するには、Catalog マイクロサービスが最終的な整合性を使用する必要があります。この整合性はおそらく、統合イベント (メッセージとイベント ベースの通信) などの非同期通信を基盤とします。 これは、 eShopOnContainers 参照アプリケーションがマイクロサービス間でこの種の整合性を実行する方法です。
CAP 定理で述べたように、可用性と ACID の強力な整合性のどちらかを選択する必要があります。 ほとんどのマイクロサービス ベースのシナリオでは、強力な一貫性ではなく、可用性と高いスケーラビリティが必要です。 ミッションクリティカルなアプリケーションは常に稼働している必要があります。開発者は強力な整合性を避け、弱い整合性や最終的な整合性に対処する手法を使用することができます。 これは、ほとんどのマイクロサービス ベースのアーキテクチャで採用されているアプローチです。
さらに、ACID スタイルまたは 2 フェーズのコミット トランザクションは、マイクロサービスの原則に反しているだけではありません。ほとんどの NoSQL データベース (Azure Cosmos DB、MongoDB など) では、分散データベースのシナリオで一般的に、2 フェーズ コミット トランザクションはサポートされません。 ただし、サービスとデータベース間でデータの一貫性を維持することが不可欠です。 この課題は、特定のデータを冗長にする必要がある場合 (たとえば、Catalog マイクロサービスと Basket マイクロサービスに製品の名前または説明が必要な場合など) に、複数のマイクロサービス間で変更を伝達する方法の問題にも関連しています。
この問題の適切な解決策は、イベント ドリブン通信を通じて明確に示されるマイクロサービスと、発行/サブスクライブ システムの間で最終的な整合性を使用することです。 これらのトピックについては、このガイドの後半の「 非同期イベントドリブン通信 」セクションで説明します。
課題 4: マイクロサービスの境界を越えて通信を設計する方法
マイクロサービスの境界を越えた通信は、本当の課題です。 このコンテキストでは、通信は使用する必要があるプロトコル (HTTP と REST、AMQP、メッセージングなど) を参照しません。 代わりに、使用する必要がある通信スタイル、特にマイクロサービスの結合方法に対処します。 結合のレベルに応じて、障害が発生した場合、その障害がシステムに与える影響は大きく異なります。
マイクロサービス ベースのアプリケーションのような分散システムでは、多数の成果物が多数移動し、多数のサーバーまたはホスト間で分散サービスを使用する場合、コンポーネントは最終的に失敗します。 部分的な障害やさらに大きな障害が発生するため、この種の分散システムの一般的なリスクを考慮してマイクロサービスとそれらの間の通信を設計する必要があります。
一般的な方法は、HTTP (REST) ベースのマイクロサービスを実装することです。そのシンプルさからです。 HTTP ベースのアプローチは完全に許容されます。ここでの問題は、使用方法に関連しています。 HTTP 要求と応答を使用して、クライアント アプリケーションまたは API ゲートウェイからマイクロサービスを操作するだけの場合は、問題ありません。 ただし、マイクロサービス間で同期 HTTP 呼び出しの長いチェーンを作成し、マイクロサービスがモノリシック アプリケーション内のオブジェクトであるかのように境界を越えて通信すると、最終的にアプリケーションで問題が発生します。
たとえば、クライアント アプリケーションが、Ordering マイクロサービスのような個々のマイクロサービスに対して HTTP API 呼び出しを行うとします。 Ordering マイクロサービスが、同じ要求/応答サイクル内で HTTP を使用して追加のマイクロサービスを呼び出す場合は、HTTP 呼び出しのチェーンを作成します。 最初は妥当と思われるかもしれません。 ただし、この道を進む際に考慮すべき重要な点があります。
ブロックと低パフォーマンス。 HTTP の同期的な性質により、すべての内部 HTTP 呼び出しが完了するまで、元の要求は応答を受け取りません。 これらの呼び出しの数が大幅に増加すると同時に、マイクロサービスへの中間 HTTP 呼び出しの 1 つがブロックされるとします。 その結果、パフォーマンスが影響を受け、追加の HTTP 要求が増加すると、全体的なスケーラビリティが指数関数的に影響を受けます。
マイクロサービスと HTTP の結合。 ビジネス マイクロサービスを他のビジネス マイクロサービスと組み合わせて使用しないでください。 理想的には、他のマイクロサービスの存在について "知っている" べきではありません。 アプリケーションが例のようにマイクロサービスの結合に依存している場合、マイクロサービスごとの自律性を実現することはほぼ不可能です。
任意の 1 つのマイクロサービスでエラーが発生しました。 HTTP 呼び出しによってリンクされたマイクロサービスのチェーンを実装した場合、いずれかのマイクロサービスが失敗すると (最終的には失敗します)、マイクロサービスのチェーン全体が失敗します。 マイクロサービス ベースのシステムは、部分的な障害時に可能な限り引き続き動作するように設計する必要があります。 指数バックオフまたはサーキット ブレーカー メカニズムを使用して再試行を使用するクライアント ロジックを実装する場合でも、HTTP 呼び出しチェーンが複雑になるほど、HTTP に基づく障害戦略を実装することが複雑になります。
実際、説明したように HTTP 要求のチェーンを作成して内部マイクロサービスが通信している場合は、モノリシック アプリケーションがあるが、プロセス内通信メカニズムではなくプロセス間の HTTP に基づいていると主張される可能性があります。
そのため、マイクロサービスの自律性を強制し、回復性を向上させるには、マイクロサービス間の要求/応答通信のチェーンの使用を最小限に抑える必要があります。 マイクロサービス間通信には、非同期メッセージベースとイベントベースの通信を使用するか、元の HTTP 要求/応答サイクルとは別に (非同期) HTTP ポーリングを使用して、非同期対話のみを使用することをお勧めします。
非同期通信の使用については、このガイドの後半で、非同期マイクロサービスの統合がマイクロサービスの自律性を維持すると非同期メッセージベースの通信というセクションでさらに詳しく説明します。
その他のリソース
データ整合性入門
https://learn.microsoft.com/previous-versions/msp-n-p/dn589800(v=pandp.10)Martin Fowler。 CQRS (コマンドとクエリの責任の分離)
https://martinfowler.com/bliki/CQRS.html具体化されたビュー
https://learn.microsoft.com/azure/architecture/patterns/materialized-viewCharles Row。 ACID と BASE: データベース トランザクション処理の pH 変動
https://www.dataversity.net/acid-vs-base-the-shifting-ph-of-database-transaction-processing/補正トランザクション
https://learn.microsoft.com/azure/architecture/patterns/compensating-transactionUdi Dahan。 サービス指向の構成
https://udidahan.com/2014/07/30/service-oriented-composition-with-video/
.NET