マイクロサービスの適切なサイズとは何でしょうか。 "大きすぎず小さすぎず" といったことをよく耳にします。確かにそのとおりなのですが、実際にはほとんど役に立ちません。 しかし、慎重に設計されたドメイン モデルから始めると、マイクロサービスについてずっと簡単に類推できます。
この記事では、継続的な例としてドローン配送サービスを使用します。 シナリオおよび対応する参照実装の詳細については、こちらを参照してください。
ドメイン モデルからマイクロサービスに
前の記事では、ドローン配送アプリケーションの境界付けられたコンテキストのセットを定義しました。 それから、これらの境界付けられたコンテキストの 1 つである "出荷" 境界付けられたコンテキストを詳しく調べて、その境界付けられたコンテキストのエンティティ、集計、ドメイン サービスのセットを明らかにしました。
その結果、ドメイン モデルからアプリケーション設計に移行する準備ができています。 ドメイン モデルからマイクロサービスを導き出すために使うことができるアプローチを次に示します。
境界付けられたコンテキストから始めます。 一般に、マイクロサービスの機能は、複数の境界付けられたコンテキストにまたがっていてはなりません。 定義上、境界付けられたコンテキストは特定のドメイン モデルの境界をマークします。 1 つのマイクロサービスに複数の異なるドメイン モデルが含まれることがわかった場合は、前に戻ってドメイン分析を修正する必要があることを示しています。
次に、ドメイン モデルの集計を確認します。 集計はマイクロサービスの適切な候補になることがよくあります。 適切に設計された集計は、次のような適切に設計されたマイクロサービスの特徴を多く備えています。
- 集計は、データ アクセスやメッセージングなどの技術的な問題ではなく、ビジネス要件から派生します。
- 集計には、機能の高い凝集度が必要です。
- 集計は永続性の境界です。
- 集計同士は疎に結合されている必要があります。
ドメイン サービスもマイクロサービスに適した候補です。 ドメイン サービスは、複数の集計に対するステートレスな操作です。 典型的な例は、複数のマイクロサービスを含むワークフローです。 ドローン配送アプリケーションで、その例を見ることができます。
最後に、機能以外の要件を検討します。 チームの規模、データの型、テクノロジ、スケーラビリティの要件、可用性の要件、セキュリティの要件などの要因を確認します。 これらの要因により、1 つのマイクロサービスを複数の小さいサービスにさらに分割したり、その逆に、複数のマイクロサービスを 1 つに結合したりすることになる場合があります。
アプリケーション内のマイクロサービスを識別した後は、次の基準に照らし合わせて設計を検証します。
- 各サービスが 1 つの役割を持っていること。
- サービス間で頻繁な呼び出しが行われないこと。 機能を 2 つのサービスに分割したことによってそれらの間で過度に頻繁に呼び出しが行われる場合、それらの関数が同じサービスに属すことを示している可能性があります。
- 各サービスが、独立して作業する小規模なチームで構築できるくらいに十分小さいこと。
- 複数のサービスをロックステップでデプロイすることが必要になる相互依存関係がないこと。 常に、他のサービスを再デプロイすることなくサービスをデプロイできる必要があります。
- サービスが密結合されておらず、個別に展開できること。
- そうすれば、サービスの境界でデータの一貫性や整合性の問題が発生することはありません。 場合によっては、単一のマイクロサービスに機能を収めることによってデータの整合性を維持することが重要になります。 ただし、強力な一貫性が本当に必要かどうかを検討する必要があります。 分散システムで最終的な整合性に対処するための戦略があり、多くの場合、サービスを分割するメリットの方が、最終的な整合性を管理する難しさより勝ります。
何よりもまず、実際的であること、そしてドメインベースの設計は反復プロセスであることを忘れないようにすることが重要です。 判断に迷うときは、より粒度の粗いマイクロサービスで始めるようにします。 1 つのマイクロサービスを 2 つの小さいサービスに分割する方が、既存の複数のマイクロサービスの機能をリファクタリングするより簡単です。
例:ドローン配送アプリケーションのマイクロサービスを定義する
開発チームが 4 つの集計 (配送、パッケージ、ドローン、アカウント) と 2 つのドメイン サービス (スケジューラとスーパーバイザー) を識別したことを思い出してください。
配送とパッケージは、マイクロサービスの明らかな候補です。 スケジューラとスーパーバイザーは、他のマイクロサービスによって実行されるアクティビティを調整するので、これらのドメイン サービスをマイクロサービスとして実装するのは当然のことです。
ドローンとアカウントが興味深いのは、他の境界付けられたコンテキストに属していることです。 1 つのオプションは、スケジューラでドローンとアカウントの境界付けられたコンテキストを直接呼び出すというものです。 もう 1 つのオプションは、発送の境界付けられたコンテキストの内部にドローンとアカウントのマイクロサービスを作成することです。 これらマイクロサービスは、発送コンテキストにより適した API またはデータ スキーマを公開することにより、境界付けられたコンテキストの間を仲介します。
ドローンとアカウントの境界付けられたコンテキストの詳細はこのガイドの範囲外なので、参照実装ではこれらのモック サービスを作成しました。 ただし、この状況では次のような要因を考慮する必要があります。
他の境界付けられたコンテキストを直接呼び出すとどのようなネットワーク オーバーヘッドがあるか。
他の境界付けられたコンテキストのデータ スキーマがこのコンテキストに適しているか、またはこの境界付けられたコンテキストに合わせて調整されたスキーマを作成する方がよいか。
他の境界付けられたコンテキストはレガシ システムか。 そうである場合は、破損対策レイヤーとして機能するサービスを作成し、レガシ システムと最新のアプリケーションの間の変換を行うことが考えられます。
チームはどのような構造か。 他の境界付けられたコンテキストを担当するチームと簡単に通信できるか。 そうでない場合は、2 つのコンテキストの間を仲介するサービスを作成すると、チーム間の通信コストの削減に役立つ場合があります。
これまで、機能以外の要件については考慮してきませんでした。 アプリケーションのスループット要件を考慮し、開発チームはクライアント要求のインジェストを行うインジェスト マイクロサービスを別に作成することを決定しました。 このマイクロサービスは、着信した要求を処理のためのバッファーに入れることで、負荷平準化を実装します。 スケジューラは、バッファーから要求を読み取って、ワークフローを実行します。
機能以外の要件により、チームは 1 つの追加サービスを作成することにしました。 これまでのサービスはすべて、パッケージのスケジューリングと配送をリアルタイムで行うプロセスに関するものでした。 しかし、システムでは、データ分析のための長期的な記憶域にすべての配送の履歴を格納する必要もあります。 チームは、これを配送サービスで行うことを検討しました。 ただし、データ記憶域の要件は、履歴分析と処理中の操作ではまったく異なります (「データに関する考慮事項」を参照)。 そのため、チームは配送履歴サービスを別に作成することにしました。このサービスは、配送サービスからの DeliveryTracking イベントをリッスンし、長期的な記憶域にイベントを書き込みます。
次の図は、この時点での設計を示したものです。
このアーキテクチャの Visio ファイルをダウンロードします。
次のステップ
この時点で、自分の設計における各マイクロサービスの目的と機能を明確に理解しているはずです。 これでシステムを設計できます。