はじめに
マイクロサービスアーキテクチャを設計する際によく語られる「分散コンピューティングの誤信(Fallacies of Distributed Computing)」の一つに 「帯域幅は無限である」 というものがあります。実際にはサービスを細かく分割するほど、サービス間通信が帯域を圧迫し、レイテンシーや信頼性にも悪影響を及ぼします。
本記事では、この問題の代表例である スタンプ結合(Stamp Coupling) と、その解決策としての コントラクト(Contract) の考え方を整理します。
モノリスとマイクロサービスの違い
モノリシックアーキテクチャでは、処理は基本的に同一プロセス内・同一メモリ空間で完結するため、ビジネスロジックの処理自体には帯域幅をまったく必要としません。
一方、マイクロサービスのような分散アーキテクチャでは、1つのビジネス要求を処理するために複数の小さなサービス(デプロイメントユニット)を横断した通信が発生します。このサービス間通信こそが帯域を消費する主因になります。
スタンプ結合とは何か
スタンプ結合とは、呼び出し元が本当に必要とするデータ以上の、大きなデータ構造(スタンプ)をまるごとやり取りしてしまう結合の形を指します。
具体例
書籍で紹介されている例は次の通りです。
- サービスA:Webサイトのウィッシュリストを管理
- サービスB:顧客プロフィール(45個の属性、合計500KB)を管理
- サービスAが本当に必要なのは、顧客の名前情報(200バイトのみ)
ウィッシュリストへのリクエストが1秒間に約2,000回発生するシステムだと仮定すると、サービスAからサービスBへの呼び出しも1秒間に2,000回発生します。1回あたり500KBを返す設計のままだと、この呼び出しだけで1秒あたり1GBもの帯域幅を消費してしまいます。
もしサービスBが本当に必要な200バイトだけを返すよう設計されていれば、消費帯域は合計でわずか400KBに抑えられます。
解決策:コントラクトによるデータ量の最適化
この問題を解消する鍵になるのが「コントラクト」です。コントラクトとは、サービス間でやり取りするデータの形式・フィールドを事前に明示的に取り決める仕組みであり、呼び出し元が本当に必要とするデータの範囲だけをレスポンスに含めることを保証します。
書籍9.4.3節で挙げられている解決策は、大きく以下の4つに整理できます。
| 手法 | 仕組み |
|---|---|
| プライベートなRESTful APIエンドポイント | 特定のコンシューマー専用に、必要なフィールドだけを返す個別エンドポイントを用意する |
| コントラクトでフィールドセレクターを使用 | リクエスト時に必要なフィールドを指定させ、その分だけレスポンスを絞り込む(例:?$select=name) |
| GraphQL | クエリ自体が「フィールド単位の契約」となり、クライアントが指定したフィールドのみ取得できる |
| CDC(Consumer Driven Contracts)との併用 | コンシューマーが必要とするデータ項目を契約として明示し、提供側がそれを守っているかテストで検証する |
フィールドセレクター
呼び出し側(コンシューマー)がリクエスト時に「どのフィールドが欲しいか」を明示的に指定できるようにし、提供側サービスはそのセレクターに従って必要な項目だけをレスポンスに含める方式です。RESTでは $select=name のようなクエリパラメータでレスポンスを絞り込む(プロジェクション)実装が代表的です。
GraphQL
GraphQLでは、クライアントがクエリの中で「取得したいフィールドだけ」を明示的に指定するのが基本仕様であり、サーバー側はクエリに含まれるフィールドのみを解決して返します。REST APIのように固定レスポンス構造を持つ必要がなく、コンシューマーごとに異なる必要フィールドへ柔軟に対応できるため、帯域が限られる環境でも効率的にデータ取得ができます。
Consumer Driven Contracts (CDC) との併用
CDCとは、各コンシューマー(呼び出し元サービス)が「自分が実際に必要とするフィールドやデータ構造」を契約として明示的に定義し、提供側サービスがその契約を守っているかを自動テストで検証する手法です。これをGraphQLのフィールドセレクターと組み合わせることで、「どのコンシューマーがどのフィールドを必要としているか」が契約として文書化・検証可能になり、スタンプ結合を構造的に防止できます。
まとめ
- モノリスでは帯域幅は問題にならないが、マイクロサービス化するとサービス間通信が帯域を大きく消費する
- スタンプ結合(必要以上に大きなデータ構造をまるごとやり取りする結合)は、この帯域消費を悪化させる典型例
- 解決策は「コントラクト」でデータのやり取りを最小限に絞ること
- プライベートなRESTfulエンドポイント
- フィールドセレクター
- GraphQL
- CDC(Consumer Driven Contracts)との併用
サービス分割を検討する際は、機能責務だけでなく 「サービス間でやり取りするデータ量」も設計の重要な観点として意識する必要があります。