次の方法で共有


コントラクト

アプリケーション アーキテクチャ: 概念的ビュー

Maarten Mullender and Mike Burner
Microsoft Corporation

July 2002
日本語版最終更新日 2002 年 12 月 3 日

目次

コントラクト入門サービス インターフェイス設計時のコントラクト実行時のコントラクトサービス インターフェイスカンバセーションコントラクトの要約注

サービスは、メッセージの送受信を行うサービス インターフェイスを通して通信を行います。サービス間通信はコントラクト に従い、このコントラクトを明示的に示しておくことによって、1 つのサービス インプリメンテーションを変更しても以前と同じように対話を行えるようになります。

コントラクト入門

2 つのサービスが対話を行うときには、メッセージが両者の間で送受信されます。両方の側が、自分が何を送信するのか、どのように送信するのか、何が送信されてくるのか、それをどのように受信するのかを正確に知っている必要があります。送信可能なメッセージの種類、そのメッセージの形式、および送信方法を定義する必要があるだけでなく、これらのメッセージの送信順序も定めることが重要です。コントラクト とは、2 つのサービス間で送信されるメッセージとその送信方法に関する定義、つまり取り決めのことです。

サービス インターフェイス

サービスは、サービス インターフェイス を通して、自分の機能を他のサービスに対して公開します。サービスにとってのサービス インターフェイスは、オブジェクトにとってのインターフェイスと似ています。オブジェクト インターフェイスはメソッド呼び出しがどのような形でなくてはならないかを定義し、サービス インターフェイスはメッセージがどのような形でなくてはならないかを定義します。どちらのタイプのインターフェイスにも、それを記述するためのフォーマットがあります。たとえば、Web Services Description Language (WSDL http://www.w3.org/TR/wsdl Cc966704.leave-ms(ja-jp,MSDN.10).gif) を使えば、COM の Interface Description Language (IDL) と同じような形でサービス インターフェイスを定義することができます。

オブジェクトが他のオブジェクトを使用したいと考えた場合には、相手側のオブジェクトのインターフェイスを知る必要があります。同じように、他のサービスのインターフェイスを使用したいと思った場合には、そのインターフェイスがどのような形なのかを知らなくてはなりません。サービスのインプリメンテーションまたはインターフェイスの記述をもとに、そのサービスとの通信を行うためのコードを生成するツールが用意されています。サービスを使用しようとしているプログラマから見ると、両者はほとんど同じものです。

サービス インターフェイスはオブジェクト インターフェイスに似ていますが、重要な違いが 1 つあります。サービス インターフェイスはインスタンス作成モデルには従っていないのです。オブジェクトは他のオブジェクトをインスタンス作成した後に、そのインターフェイスを呼び出します。その後は、同じオブジェクトに対して複数の呼び出しを行うことができます。一方、サービスは単にサービスに対してメッセージを送信します。複数のメッセージを送信した場合、インフラストラクチャはこれらのメッセージを複数の異なる物理的位置に送信することがありますが、一般にクライアントはその違いに気づきません。他のオブジェクトのリモートでの呼び出しは、一般にリモート プロシージャ呼び出し (RPC) と呼ばれます。

サービス インターフェイスは、メッセージが操作する必要のあるすべての情報を記述しています。これには、サービスが提供している関数と、これらの関数のパラメータおよび戻り値が含まれます。要求または応答は動詞、パラメータは主語と考えることができます。これらの主語は、ビジネス ドキュメントドキュメント などと呼ばれることがあります。

コンシューマとサービスは互いに通信を行うので、両方の側がメッセージの送信と受信を行うことができます。したがって、コンシューマはサービスでもなくてはならず、サービス インターフェイスは着信メッセージと発信メッセージの両方を定義していなくてはなりません。サービス インターフェイスは、対応するペアとして定義されます。最も単純な対応関係の例としては、要求の送信と応答の受信を行うことができる従来型のプロキシがあります。

サービス インターフェイスの扱い、インスタンス作成モデルなしでの処理、時間と空間内で分散している対話の扱い、非同期的な、ときには信頼性のないメッセージングの処理、そして複雑な例外処理への対応は、サービスの設計を難しくしている要因です。信頼できるサービスの設計には時間がかかり、したがってサービス設計のコストは高くなります。

設計時のコントラクト

2 つのサービスが対話を行うときには、メッセージが両者の間で送受信されます。送信可能なメッセージの種類、そのメッセージの形式、および送信方法を定義する必要があるだけでなく、これらのメッセージの送信順序も定めることが重要です。

たとえば、購入オーダーを送信した購入側が、オーダーの受信確認を販売側から受け取るようになっているとします。販売側は、たとえば 3 日以内にオーダー確認を送信し、オーダーが受け付けられて、処理が進行中であることを通知します。購入側は、この確認を受信するまでの間はオーダーをキャンセルすることができます。購入側は、この両方のメッセージを理解し、予期していなくてはなりません。また販売側としては、注文された商品の在庫がない場合には、部分的な確認や、代わりの商品の提案を送信できるようになっていると便利でしょう。

購入側と販売側の間の対話はかなり複雑になり、この対話が両当事者間で同じように理解されていなければ、やがては何らかの問題が生じます。

個々の設計を始める前に、両当事者はいくつかの前提条件について合意しなくてはなりません。

  • この対話の目的は何か?
  • 対話にはどのような当事者または役割が参加するのか?
  • どのようなメッセージが使用されるのか?
  • これらのメッセージの形式は?
  • メッセージの順序としてはどのようなものが考えられるか?
  • 各ステップの後の考えうる処理は?
  • どのようなビジネス例外が発生する可能性があるか?
  • ビジネス例外または技術的例外が発生したときにはどうするか?

対話に関与する役割をもとに、コントラクトによってメッセージ、メッセージ形式、およびその発生順序が定義されます。メッセージ シーケンスには、発生しうる例外と、これらの例外に対する対応が含まれていることに注意してください。例外は各当事者またはインフラストラクチャによって生成されることがあります。たとえば、片方の当事者が一定の時間内に応答しなければ、これはビジネス例外と見なすことができます。一方、メッセージを一定の時間内に配信できない場合には、技術的例外が発生します。これらの例外の処理方法は、一般に、どのようなビジネス ケースを解決しようとしているのかによって変わります。数日以内に到着するはずの応答が到着しないというケースでは、技術的例外のせいで配信が妨げられているという可能性は低いでしょう。応答が数秒以内に必要な場合には、コントラクトに対処方法が明確に定義されている必要があります。

各当事者は、コントラクトの中のそれぞれの役割をインプリメントする必要があります。上の例では、プロセスは購入者の役割または販売者の役割をインプリメントしています。コントラクトは一般に両側のプロセスによってインプリメントされます。このようなプロセスは、メッセージの送信とメッセージの受信のために定義されたアクションを持っており、どのメッセージを送信または予期すべきかを定義する意思決定ポイントを持っています。また、プロセスは外からは見えないアクションも定義します。たとえば、オーダーが受信されたとき、プロセスはその品目の在庫があるかどうか、購入者にどれだけのリベートの資格があるかをチェックするかもしれません。応答はこれらの内部的なアクションによって決まりますが、意思決定ポイントは外の世界に対しては公開されません。相手側は数種類の反応がありうることを理解していますが、実際の意思決定アルゴリズムはサービス インターフェイスによって隠蔽されています。

図 1 は、通信を行う 2 つのサービスの間のコントラクトを示しています。

図 1. コントラクト

図 1. コントラクト

サービス インターフェイスは、メッセージとメッセージ形式を定義するだけでなく、順序の制約や、タイムアウト時間、例外、およびキューイングなどのトランスポートの要件といった制約も定義します。これは、サービスがコントラクト内で演じる役割の定義です。

サービスは、他の複数のサービスと通信を行うために複数のインターフェイスを使用することができます (また実際に頻繁に使用します)。たとえば、購入オーダーを受け取るサービスは、倉庫サービスや経理サービスなどと通信を行わなくてはならないでしょう。

設計時のコントラクトの定義

サービスを定義するときには、そのサービスが送受信するメッセージと、これらのメッセージのおおよそのシーケンスを識別する必要があります。また、タイムアウト例外などの発生する可能性のある例外と、これらの例外の処理方法を決定するべきです。その後、サービスの外部の動作 (インターフェイス) が定義され、したがってコントラクトを決定することが可能となります。これが終わったら、インプリメンテーション コードを実際に作成することができます。

アプリケーション ライフサイクルの設計段階で、コントラクトを正しく識別し、定義することは、見返りのある投資です。これには以下の利点があります。

  • 正しい動作のサービスを構築できる。サービスのインターフェイスとメッセージ シーケンスの両方を定義しておけば、そのコントラクトをカンバセーションの両方の側でインプリメントすることができます。

  • 交換可能なサービスを構築できる。コントラクトの片方の当事者を変更しても、両当事者がコントラクトに従っている限り何の問題も生じません。

    たとえば、レガシー システムのためのラッパーを作成し、そのラッパーと通信を行うサービスを構築しなくてはならないことがあります。サービスはラッパーのサービス インターフェイスを通して通信を行うので、レガシー システムは、後に同じインターフェイスを公開している別のサービスに簡単に置き換えることができます。別の例としては、大企業が複数のサプライヤと取引を行っている場合があります。企業はサービス インターフェイスを定義し、すべてのサプライヤはそのサービス インターフェイスを使用することを義務付けられます。サービスのインプリメンテーションは、サプライヤに影響を与えることなく変更できます。

  • どのサービスが連動するのかを識別できる。サービスが既知のサービス インターフェイスのセットを持っていれば、そのサービスと通信できる他のサービスを簡単に識別することができます。図 2 にその様子を示します。

    図 2. ペアとなるサービス間での状態の共有

    図 2. ペアとなるサービス間での状態の共有

  • 設計と管理をサポートするツールを構築できる。CASE ツールを作成しているとき、各サービスのインターフェイスと役割が適切に設計されていれば、それらが構築後に正しく通信を行えると仮定することができます。各種のケーブルをコントラクトと見なして、どのデバイスを互いにリンクできるかを識別することができます。

実行時のコントラクト

これまで、このドキュメントでは、コントラクトの設計時の側面に焦点を当ててきました。メッセージ フロー、メッセージ形式、およびトランスポートの信頼できる動作は、設計に含まれる要素です。コントラクトのその他の多くの側面は、サービスを配置した組織にしか見えず、一般に組織間の折衝の結果であったり、さらにはこれらの組織内の複数のグループ間の折衝の全体的な結果であったりします。1 日当たりに処理される要求の数、トランスポート プロトコルの種類、およびサポートされるエンコーディングのタイプなどを定義するサービス レベル契約は、認証メカニズム、暗号化、および署名などのセキュリティ上の制約とは別の責任に属します。それでも、これらすべては実行時のコントラクトの一部であり、そのコントラクトの中の個々の項目と見なすことができます。コントラクトは、2 つのサービス間の通信のすべての側面を定義するものなのです。

サービス インターフェイス

前に述べたように、サービス インターフェイスはコントラクトをインプリメントします。設計時のコントラクトの大部分は、開発フェーズにおいてコード内にインプリメントされます。メッセージの順序はほぼつねにビジネス ロジックによって指定され、そのビジネス ロジックの設計に含まれています。しかし、コントラクトの多くの部分がインフラストラクチャによって提供されることもあります。インフラストラクチャは論理アドレスを物理アドレスにマップし、認証メカニズム、負荷分散、およびフェイルオーバーを提供することがあります。もちろん、インフラストラクチャはビジネス ロジックによって課せられた制約の実施を支援することもあります。

一般に、サービス インターフェイスはコントラクトのディスカバリ メカニズムを提供します。Web Services Description Language (WSDL) 標準はいくつかのディスカバリ メカニズムを定めており、今後も新たなものが提供される予定です。このように、サービス インターフェイスはコントラクトを有効にし、実施するだけでなく、コントラクトに関する情報を提供して、他のサービスがコントラクトに従えるようにします。

インフラストラクチャは、サービス インターフェイスをインプリメントするために、実行時と設計時の両方の情報を必要とします。

サービスは複数のインターフェイスを持つことができます。コントラクトは、これらのインターフェイスのうちの 1 つと、他のサービスの 1 つのインターフェイスとの間の対話を定義します。より正確に述べれば、設計時コントラクトは 2 つのインターフェイスの間の対話を定義します。設計時コントラクトにより、設計者とプログラマは正しいサービスを構築することができます。実行時コントラクトは、ポート またはアドレスのペアに対して制約を付加します。実行時コントラクトにより、情報テクノロジ (IT) 部門は、望ましい実行時制約を表現し、他人に伝達することができます。このドキュメントの残りの部分では、サービス インターフェイスコントラクト という言葉を、特に記していない限り設計時と実行時の両方を指すものとして使用します。

カンバセーション

メッセージの送受信を行っている 2 つのサービスは、カンバセーション を行っています。カンバセーションはコントラクトのインスタンスであり、コントラクト内に定義されたプロトコルに従わなくてはなりません。サービスは、追加の検証なしにカンバセーションを実行することができます。ただし、インフラストラクチャがカンバセーションをコントラクトと照合して検証するようになっていれば、例外処理の一貫性が高まり、サービスの開発は簡単になります。

この検証はいくつかの場所で行うことができます。コントラクトの片側または両側で行うことができますし、すべての当事者が共有する、コントラクトの中央のインスタンスを作成することも可能です。コントラクトの違反があった場合には、関連する当事者に対して、明確に定義された例外が送信されるようにする必要があります。

インフラストラクチャは、カンバセーションをコントラクトと照らし合わせて検証するだけでなく、カンバセーションの状態に関する情報を提供することもできます。これは当事者間で同期を取るのに便利ですが、当事者のうちの 1 つがたとえば汎用のユーザー インターフェイス アプリケーションであるような場合にはさらに便利になります。アプリケーションはインフラストラクチャに対して、カンバセーションの次のステップとしてはどのようなものが考えられるかを問い合わせ、ユーザーに対して適切なボタンを表示することができます。別のシナリオとして、経費報告サービス インターフェイスを使用するスプレッドシート アプリケーションは、カンバセーションの特定のポイントで、サービスがどのようなアクションを公開しているかを知る必要があります。これと似たことは、当事者の 1 つがプロセスではなく、状態を格納することのできない汎用的なコンポーネントである場合にも起こります。たとえば、デスクトップ コンピュータを使ってオーダーを開始し、その作業を PDA 上で続けなくてはならないような場合です。この PDA を、プロセスの正しい段階でカンバセーションに接続する何らかの手段がなくてはなりません。

サービス間のカンバセーションを監視して、カンバセーションの状態に関する情報を入手することができます。これは、サービスのビジネス ロジックを通して情報を要求する、オーダーの状態に対するクエリとは異なります。つまり、ビジネス ロジックを含んでいるインフラストラクチャ ソフトウェアに対して、コントラクトの中で次にどのようなステップが起こりうるかといった情報を要求することができるのです。

コントラクトの要約

サービス インターフェイスは、サービスに送信されるメッセージがどのような形でなくてはならないかを定義します。これは、カンバセーションの両側に位置するペアとして作成されます。

2 つのサービス間で送信されるメッセージに関わるすべての情報は、コントラクトによって定義されます。これには、メッセージの送受信の順序、エラーが発生したときに生成すべき例外、メッセージに使用される形式、およびセキュリティを確保する方法などが含まれます。

設計フェーズでコントラクトを適切に定義することにより、使いやすく、管理しやすいサービスを作成することができます。

  • SOAP とそのすべての関連標準では、ポートとポート タイプという用語が使用されています。ポートとは、サービスが受信しているメッセージのエンドポイントの名前付きの位置、すなわち Uniform Resource Locator (URL) です。ポート タイプは、このドキュメントでサービス インターフェイス と呼んでいるものです。一般にプログラミング言語はインターフェイスという言葉を使うのに対し、Simple Object Access Protocol (SOAP) などの通信プロトコルはポート タイプという言葉を使用します。

ページのトップへ  ページのトップへ