消費者主導契約:サービス進化パターン
この記事では、サービスプロバイダと消費者のコミュニティを進化させる上での課題について論じています。サービスプロバイダが契約の一部、特にドキュメントスキーマを変更した場合に発生するカップリングの問題について説明し、そのような問題を軽減するための2つのよく知られた戦略——スキーマ拡張ポイントの追加と受信メッセージの「必要十分な」検証の実行——を特定します。どちらの戦略も、プロバイダ契約の変更から消費者を保護するのに役立ちますが、どちらの戦略も、プロバイダがどのように使用されているか、そして進化する際に維持しなければならない義務について、プロバイダに洞察を与えません。これらの軽減戦略の1つである「必要十分な」検証戦略のassertion-based languageに基づいて、この記事では「消費者主導契約」パターンについて説明します。このパターンは、プロバイダに消費者義務に関する洞察を与え、消費者が求める主要なビジネス機能の提供を中心にサービスの進化を促進します。
2006年6月12日
最初の公開以降、この記事にはいくつかの更新があります。Thoughtworks Anthologyと時折のブログに更新版があります。
サービスの進化:事例
サービスの進化に伴う問題のいくつかを説明するために、消費者アプリケーションが製品カタログを検索できるようにする、単純なProductSearchサービスを考えてみましょう。検索結果は次の構造をしています

図1:検索結果スキーマ
検索結果のドキュメントの例を次に示します。
<?xml version="1.0" encoding="utf-8"?> <Products xmlns="urn:example.com:productsearch:products"> <Product> <CatalogueID>101</CatalogueID> <Name>Widget</Name> <Price>10.99</Price> <Manufacturer>Company A</Manufacturer> <InStock>Yes</InStock> </Product> <Product> <CatalogueID>300</CatalogueID> <Name>Fooble</Name> <Price>2.00</Price> <Manufacturer>Company B</Manufacturer> <InStock>No</InStock> </Product> </Products>
ProductSearchサービスは現在、内部マーケティングアプリケーションと外部再販業者のWebアプリケーションの2つのアプリケーションで使用されています。両方の消費者は、受信したドキュメントを処理する前に、XSD検証を使用してドキュメントを検証します。内部アプリケーションは、CatalogueID、Name、Price、Manufacturerフィールドを使用します。外部アプリケーションは、CatalogueID、Name、Priceフィールドを使用します。どちらもInStockフィールドを使用しません。マーケティングアプリケーション用に検討されましたが、開発ライフサイクルの初期段階で削除されました。
サービスを進化させる最も一般的な方法の1つは、1つ以上の消費者のためにドキュメントに追加フィールドを追加することです。プロバイダと消費者の実装方法によっては、このような単純な変更でも、ビジネスとそのパートナーにとって高コストな影響を与える可能性があります。
例として、ProductSearchサービスが運用されてからしばらく経った後、2番目の再販業者がサービスの使用を検討し、各製品にDescriptionフィールドを追加することを要求したとします。消費者の構築方法により、この変更はプロバイダと既存の消費者にとって重大で高コストな影響を及ぼします。変更のコストは、変更の実装方法によって異なります。サービスコミュニティのメンバー間で変更のコストを分配する方法は少なくとも2つあります。まず、元のスキーマを変更し、各消費者がスキーマのコピーを更新して検索結果を正しく検証できるように要求できます。システムの変更コストは、このようの変更要求に直面した場合、常に何らかの変更を行う必要があるプロバイダと、更新された機能に関心のない消費者との間で分配されます。あるいは、新しい消費者のためにサービスプロバイダに2番目の操作とスキーマを追加し、既存の消費者のために元の操作とスキーマを維持することもできます。変更コストはプロバイダに限定されますが、サービスをより複雑にし、維持コストが高くなります。
インターリュード:サービスの負担
企業のアプリケーション環境をサービス化する主なメリットには、組織の俊敏性の向上と変更実装の総コストの削減があります。SOAは、高価値のビジネス機能を個別で再利用可能なサービスに配置し、これらのサービスを接続してオーケストレーションすることにより、コアビジネスプロセスを満たすことで、組織の俊敏性を向上させます。サービス間の依存関係を削減し、変更や予期せぬイベントへの対応として迅速に再構成および調整できるようにすることで、変更コストを削減します。
ただし、ビジネスがこれらのメリットを完全に実現できるのは、SOAがサービスを互いに独立して進化させることができる場合のみです。サービスの独立性を高めるために、型ではなく契約を共有するサービスを構築します。それにもかかわらず、主に消費者がプロバイダの契約の特定のバージョンに依存しているため、多くの場合、サービスプロバイダと同じ速度で消費者を進化させる必要があります。最終的に、サービスプロバイダは、提供する契約の要素を変更することに慎重なアプローチをとるようになります。これは、部分的には、消費者がこの契約を実現する方法を予測したり、洞察を得ることができないためです。最悪の場合、サービス消費者はプロバイダ契約を実現し、ドキュメントスキーマ全体を内部ロジックで単純に表現することでプロバイダに結び付けられます。
契約はサービスの独立性を可能にします。しかし、逆説的に、サービスプロバイダと消費者を望ましくない方法で結合することもあります。SOAで実装する契約の機能と役割を内省せずに、サービスはめったに体系的に対処できない「隠れた」カップリングの一種の影響を受けます。サービスのコミュニティが契約を採用した方法に関するプログラムによる洞察の欠如と、サービスプロバイダと消費者が行う実装の選択に関する制約の欠如は、SOAによるエンタープライズ化のメリットを損なうことにつながります。要するに、企業はサービスの負担を負うことになります。
スキーマのバージョン管理
ProductSearchサービスを悩ませる契約とカップリングの問題に関する調査を始めるには、スキーマのバージョン管理の問題を見てみましょう。WC3技術アーキテクチャグループ(TAG)は、カップリングの問題を軽減する方法でサービスのメッセージスキーマを進化させるのに役立つ、いくつかのバージョン管理戦略について説明しています。これらの戦略は、サービスがスキーマの異なるバージョンを区別してはならず、そのためすべての変更を許容しなければならないと規定する過度に自由なnoneから、メッセージの予期しないバージョンを受信した場合にサービスが中止しなければならないと規定する非常に保守的なbig bangまでさまざまです。
どちらの極端にも、ビジネス価値の提供を妨げ、システムの総保有コストを増大させる問題があります。明示的および暗黙的な「バージョン管理なし」戦略は、相互作用が予測不可能で、脆弱で、下流で変更のコストがかかる点で類似したシステムにつながります。一方、ビッグバン戦略は、スキーマの変更がプロバイダと消費者に波及し、アップタイムを中断し、進化を遅らせ、収益を生む機会を減少させる、密接に結合されたサービス環境を生み出します。
私たちの例のサービスコミュニティは、効果的にビッグバン戦略を実装しています。システムのビジネス価値を高めることに関連するコストを考えると、プロバイダと消費者は、より柔軟なバージョン管理戦略、つまりTAGの調査結果でcompatible戦略と呼ばれるものから利益を得ることが明らかです。この戦略は、下位互換性のあるスキーマと上位互換性のあるスキーマを提供します。サービスの進化という文脈では、下位互換性のあるスキーマにより、新しいスキーマの消費者は古いスキーマのインスタンスを受け入れることができます。たとえば、下位互換性のある新しいバージョンのリクエストを処理するように構築されたサービスプロバイダは、それでも古いスキーマに従ってフォーマットされたリクエストを受け入れることができます。一方、上位互換性のあるスキーマは、古いスキーマの消費者が新しいスキーマのインスタンスを処理できるようにします。これは、既存のProductSearch消費者の問題点です。検索結果スキーマが最初に運用されたときに上位互換性を持たせていれば、消費者は新しいバージョンの検索結果のインスタンスを中断したり修正したりすることなく処理できます。
拡張ポイント
スキーマを下位互換性と上位互換性の両方にすることは、よく知られた設計タスクであり、拡張性のMust Ignoreパターンによって最適に表現されます(David OrchardとDare Obasanjoの論文を参照)。Must Ignoreパターンは、スキーマに拡張ポイントを含めることを推奨しています。これにより、型に拡張要素を追加し、各要素に追加属性を追加できます。このパターンは、消費者が拡張を処理する方法を指定する処理モデルをXML言語が定義することも推奨しています。最も単純なモデルでは、消費者が認識しない要素を無視する必要があります(パターンの名前の由来)。モデルは、消費者が「必須理解」フラグを持つ要素を処理すること、または理解できない場合は中止することも要求する場合があります。
これは、もともと検索結果ドキュメントの基礎としたスキーマです。
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="InStock" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:schema>
時間を巻き戻し、サービスのライフタイムの最初から、上位互換性のある拡張可能なスキーマを指定してみましょう。
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="InStock" type="xs:string" /> <xs:element minOccurs="0" maxOccurs="1" name="Extension" type="Extension" /> </xs:sequence> </xs:complexType> <xs:complexType name="Extension"> <xs:sequence> <xs:any minOccurs="1" maxOccurs="unbounded" namespace="##targetNamespace" processContents="lax" /> </xs:sequence> </xs:complexType> </xs:schema>
このスキーマには、各製品の最後にオプションのExtension要素が含まれています。拡張要素自体は、ターゲット名前空間からの1つ以上の要素を含むことができます。

図2:拡張可能な検索結果スキーマ
これで、各製品に説明を追加する変更要求を受け取ると、プロバイダが拡張コンテナに挿入する追加のDescription要素を含む新しいスキーマを公開できます。これにより、ProductSearchサービスは製品の説明を含む結果を返し、新しいスキーマを使用する消費者はドキュメント全体を検証できます。古いスキーマを使用する消費者は中断しませんが、説明は処理しません。新しい結果ドキュメントは次のようになります。
<?xml version="1.0" encoding="utf-8"?> <Products xmlns="urn:example.com:productsearch:products"> <Product> <CatalogueID>101</CatalogueID> <Name>Widget</Name> <Price>10.99</Price> <Manufacturer>Company A</Manufacturer> <InStock>Yes</InStock> <Extension> <Description>Our top of the range widget</Description> </Extension> </Product> <Product> <CatalogueID>300</CatalogueID> <Name>Fooble</Name> <Price>2.00</Price> <Manufacturer>Company B</Manufacturer> <InStock>No</InStock> <Extension> <Description>Our bargain fooble</Description> </Extension> </Product> </Products>
改訂されたスキーマは次のようになります。
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="InStock" type="xs:string" /> <xs:element minOccurs="0" maxOccurs="1" name="Extension" type="Extension" /> </xs:sequence> </xs:complexType> <xs:complexType name="Extension"> <xs:sequence> <xs:any minOccurs="1" maxOccurs="unbounded" namespace="##targetNamespace" processContents="lax" /> </xs:sequence> </xs:complexType> <xs:element name="Description" type="xs:string" /> </xs:schema>
拡張可能なスキーマの最初のバージョンは2番目と上位互換性があり、2番目は1番目と下位互換性があることに注意してください。ただし、この柔軟性は、複雑さの増大を犠牲にして得られます。拡張可能なスキーマを使用すると、XML言語に予期しない変更を加えることができますが、同時に、決して発生しない可能性のある要件も提供します。そうすることで、単純な設計から得られる表現力を曖昧にし、ドメイン言語にメタ情報コンテナ要素を導入することで、ビジネス情報の有意義な表現を妨げます。
スキーマの拡張性については、ここではこれ以上議論しません。重要なのは、拡張ポイントによって、サービスプロバイダとコンシューマを壊すことなく、スキーマとドキュメントに対して後方互換性のある変更と前方互換性のある変更を行うことができるということです。しかし、スキーマ拡張は、契約に本質的に破壊的な変更を加える必要がある場合のシステムの進化を管理するのに役立ちません。
破壊的変更
付加価値として、ProductSearchサービスは検索結果に、製品が現在在庫にあるかどうかを示すフィールドを含んでいます。このサービスは、レガシー在庫システムへの高コストな呼び出しを使用してこのフィールドに値を入力します。この依存関係は維持コストが高いため、サービスプロバイダは、この依存関係を削除し、設計を整理し、システムの全体的なパフォーマンスを向上させたいと考えています。できれば、変更のコストをコンシューマに押し付けることなく実現したいと考えています。コンシューマのオーナーと話し合った結果、どのコンシューマアプリケーションもこの値を実際に使用していないことがわかりました。高コストであるにもかかわらず、冗長なのです。
残念ながら、現在の設定では、必須コンポーネント(この場合は *InStock* フィールド)を拡張可能なスキーマから削除すると、既存のコンシューマが壊れてしまいます。プロバイダを修正するには、システム全体を修正する必要があります。プロバイダから機能を削除して新しい契約を公開すると、各コンシューマアプリケーションを新しいスキーマで再展開し、サービス間の相互作用を徹底的にテストする必要があります。この点で、ProductSearchサービスはコンシューマから独立して進化することはできません。プロバイダとコンシューマはすべて同時に変更しなければなりません。
サービスコミュニティは、各コンシューマがプロバイダ契約全体をコンシューマの内部ロジックに無邪気に反映した「隠れた」カップリングの一種を実装しているため、進化に苦労しています。コンシューマは、XSD検証、そしてある程度はドキュメントスキーマから派生した静的言語バインディングの使用を通じて、コンポーネントの処理の必要性とは関係なく、プロバイダ契約全体を暗黙的に受け入れています。
David Orchardは、インターネットプロトコルの堅牢性原則「一般的に、実装は送信動作においては保守的で、受信動作においては寛容でなければならない」に触れることで、この問題を回避する方法の手がかりを提供しています。サービスの進化の文脈において、この原則を拡張して、メッセージ受信側は「ちょうど十分な」検証を実装するべきである、つまり、実装するビジネス機能に貢献するデータのみを処理し、受信したデータに対して明示的に境界付けられた、またはターゲットを絞った検証のみを実行するべきである、と述べることができます。これは、XSD処理に固有の暗黙的な無制限の「オールオアナッシング」検証とは対照的です。
Schematron
コンシューマ側の検証をターゲット化または境界付ける方法の1つは、受信メッセージのドキュメントツリー軸に沿ってパターン式をアサートすることです。おそらく、Schematronのような構造ツリーパターン検証言語を使用します。Schematronを使用すると、ProductSearchサービスの各コンシューマは、検索結果で期待されるものをプログラム的にアサートできます。
<?xml version="1.0" encoding="utf-8" ?> <schema xmlns="http://www.ascc.net/xml/schematron"> <title>ProductSearch</title> <ns uri="urn:example.com:productsearch:products" prefix="p"/> <pattern name="Validate search results"> <rule context="*//p:Product"> <assert test="p:CatalogueID">Must contain CatalogueID node</assert> <assert test="p:Name">Must contain Name node</assert> <assert test="p:Price">Must contain Price node</assert> </rule> </pattern> </schema>
Schematronの実装は、通常、このようなSchematronスキーマをXSLT変換に変換します。メッセージ受信側は、ドキュメントの有効性を判断するためにこの変換をドキュメントに適用できます。
このSchematronスキーマの例では、コンシューマアプリケーションが処理する必要がない基礎となるドキュメント内の要素については、アサーションを行っていません。このように、検証言語は明示的に必要な要素の境界付けられたセットをターゲットにしています。基礎となるドキュメントのスキーマへの変更は、以前は必須だった要素の廃止または削除を含む変更であっても、Schematronスキーマで記述された明示的な期待に影響を与えない限り、検証プロセスでは検出されません。
これは、契約とカップリングの問題に対する比較的軽量なソリューションであり、ドキュメントに分かりにくいメタ情報要素を追加する必要はありません。そのため、もう一度時間を巻き戻し、記事の冒頭で説明した単純なスキーマを復活させましょう。しかし、今回は、コンシューマが受信動作において寛容であり、実装するビジネス機能をサポートする情報のみを検証および処理する(受信メッセージの検証にはXSDではなくSchematronスキーマを使用する)ことを要求します。これで、プロバイダが各製品の説明を追加するように求められた場合、サービスは既存のコンシューマを邪魔することなく、改訂されたスキーマを公開できます。同様に、*InStock*フィールドがどのコンシューマによっても検証または処理されていないことがわかった場合、サービスは検索結果スキーマを改訂できます。これも、各コンシューマの進化速度を妨げることはありません。
このプロセスの最後に、ProductSearchの結果スキーマは次のようになります。
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="Description" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:schema>
消費者主導契約
上記の例におけるSchematronの使用は、プロバイダとコンシューマ間の契約に関するいくつかの興味深い観察結果につながり、ドキュメント検証を超えた意味合いがあります。このセクションでは、これらの洞察の一部を抽出し、一般化し、 *コンシューマ主導契約* と呼ぶパターンで表現します。
まず注意すべきことは、ドキュメントスキーマは、サービスプロバイダがコンシューマに提供して、その機能を利用できるようにするもののほんの一部であるということです。これらの外部化された利用ポイントの合計を *プロバイダ契約* と呼びます。
プロバイダ契約
プロバイダ契約は、その機能をサポートするために必要なエクスポート可能な要素のセットという観点から、サービスプロバイダのビジネス機能能力を表現します。サービスの進化の観点から、契約とはエクスポート可能なビジネス機能要素のセットのためのコンテナです。これらの要素の非規範的なリストには以下が含まれます。
- **ドキュメントスキーマ** ドキュメントスキーマについては、すでに詳細に説明しました。インターフェースに次いで、ドキュメントスキーマは、サービスの進化に伴って変更される可能性が最も高いプロバイダ契約の一部です。しかし、おそらくこのため、拡張ポイントやドキュメントツリーパスのアサーションなどのサービス進化戦略を最も多く適用してきた部分でもあります。
- **インターフェース** 最も単純な形式では、サービスプロバイダインターフェースは、コンシューマがプロバイダの動作を駆動するために利用できるエクスポート可能な操作シグネチャのセットで構成されます。メッセージ指向システムは、通常、比較的単純な操作シグネチャをエクスポートし、ビジネスインテリジェンスを交換するメッセージにプッシュします。メッセージ指向システムでは、受信メッセージは、メッセージヘッダーまたはペイロードにエンコードされたセマンティクスに従ってエンドポイントの動作を駆動します。一方、RPCのようなサービスは、ビジネスセマンティクスの多くを操作シグネチャにエンコードします。いずれの場合も、コンシューマはプロバイダのインターフェースの一部に依存してビジネス価値を実現するため、サービス環境を進化させる際には、インターフェースの消費を考慮する必要があります。
- **会話** サービスプロバイダとコンシューマは、リクエストレスポンスやファイアアンドフォーゲットなどの1つ以上のメッセージ交換パターンで構成される会話でメッセージを交換します。会話の中で、コンシューマは、プロバイダが送信および受信するメッセージにおいて、相互作用に固有のステートを外部化する必要があると期待する可能性があります。たとえば、ホテル予約サービスは、コンシューマに、会話の開始時に部屋を予約し、後続のメッセージ交換で予約を確認し、デポジットを行う機能を提供する可能性があります。ここで、コンシューマは、プロセスにおける各ステップで当事者が会話全体を繰り返すことを要求するのではなく、これらの後続の交換を行う際に、サービスが予約の詳細を「覚えている」ことを当然期待する可能性があります。サービスの進化に伴い、プロバイダとコンシューマが利用できる会話のやり取りのセットは変わる可能性があります。したがって、会話は、プロバイダ契約の一部として考慮される候補です。
- **ポリシー** サービスプロバイダは、ドキュメントスキーマ、インターフェース、会話をエクスポートするだけでなく、契約の他の要素を実現する方法を規定する特定の使用要件を宣言および適用する可能性があります。最も一般的には、これらの要件は、コンシューマがプロバイダの機能を利用できるセキュリティとトランザクションコンテキストに関連しています。Webサービススタックは、通常、WS-Policy汎用モデルとWS-SecurityPolicyなどの追加のドメイン固有のポリシー言語を使用してこのポリシーフレームワークを表現しますが、プロバイダ契約に含める候補としてポリシーを検討する文脈では、ポリシーの定義は仕様と実装に依存しません。
- **サービス品質特性** サービスプロバイダとコンシューマが利用するビジネス価値の可能性は、多くの場合、可用性、待ち時間、スループットなどの特定のサービス品質特性のコンテキストで評価されます。これらの特性をプロバイダ契約の構成要素として考慮し、サービス進化戦略で考慮する必要があります。
ここの契約の定義は、サービスについて話すときに通常提供する定義よりも少し広範囲ですが、サービス進化の観点からは、問題領域に影響を与える重要な力を効果的に抽象化します。ただし、この定義は、プロバイダ契約に含める可能性のある要素の種類に関して網羅的なものではありません。サービス進化戦略に含める候補となるエクスポート可能なビジネス機能要素の論理的なセットを指すだけです。論理的な観点からは、この候補要素のセットはオープンですが、実際には、相互運用性要件やプラットフォームの制限などの内部または外部要因によって、契約に含めることができる要素の種類が制約される場合があります。たとえば、WS-Basicプロファイルに準拠するサービスに属する契約には、ポリシー要素が含まれていない可能性があります。
このような制約にかかわらず、契約の範囲は、メンバー要素の凝集度によって単に決定されます。契約には多くの要素を含めることができ、範囲が広い場合もあれば、ビジネス機能能力を表現する限り、ごく少数の要素に絞って狭い範囲に焦点を当てることもできます。
候補となる契約要素をプロバイダ契約に含めるかどうかをどのように決定しますか?その要素によってカプセル化されたビジネス機能能力がサービスのライフサイクル全体を通して満たされ続けるという1つ以上の期待を、コンシューマが妥当に表現できるかどうかを自問自答することで決定します。すでに、例のサービスのコンシューマが、サービスによってエクスポートされるドキュメントスキーマの一部に関心を示し、この契約要素に関する期待が引き続き満たされていることをどのようにアサートできるかを確認しました。したがって、ドキュメントスキーマはプロバイダ契約の一部です。
プロバイダ契約には、次の特性があります。
- **閉じられていて完全である** プロバイダ契約は、コンシューマが利用できるエクスポート可能な要素の完全なセットという観点から、サービスのビジネス機能能力を表現し、したがって、システムで使用可能な機能に関して閉じられていて完全です。
- **単一で権威がある** プロバイダ契約は、システムで使用可能なビジネス機能の表現において、単一で権威があります。
- **境界付けられた安定性と不変性** プロバイダ契約は、境界付けられた期間および/または場所で安定していて不変です(Pat Hellandの論文 *Data on the Outside vs. Data on the Inside* の「境界付けられた空間と時間におけるデータの有効性」セクションを参照)。プロバイダ契約は、通常、何らかの形式のバージョン管理を使用して、異なる境界付けられた契約のインスタンスを区別します。
消費者契約
サービスの進化において公開するスキーマに関する顧客の期待を考慮し、プロバイダがそれらを知る価値があると判断した場合、それらの顧客の期待をプロバイダに取り込む必要があります。 この例におけるSchematronアサーションは、プロバイダが実装すれば、プロバイダが顧客とのコミットメントを継続的に満たすのに役立つ可能性のあるテストの種類と非常に似ています。 プロバイダはこれらのテストを実装することで、サービスコミュニティ内の既存の機能を壊すことなく、生成するメッセージの構造をどのように進化させることができるかをより深く理解できます。そして、提案された変更が実際に1つ以上の顧客を壊す場合、プロバイダは問題を即座に認識し、関係者とより適切に対処し、ビジネスの状況に応じて顧客の要件に対応したり、変更のためのインセンティブを提供したりすることができます。
この例では、すべての顧客によって生成されたアサーションのセットは、アサーションが親アプリケーションに対して有効である期間中に交換されるメッセージの必須構造を表していると考えることができます。 プロバイダがこのアサーションのセットを持っていれば、アサーションのセットが有効かつ完全である限り、送信するすべてのメッセージがすべての顧客に対して有効であることを保証できます。
この構造を一般化すると、すでに述べたプロバイダ契約と、プロバイダと顧客の関係のインスタンスで得られる個々の契約上の義務(ここでは顧客契約と呼びます)を区別することができます。 プロバイダが顧客が表明した妥当な期待を受け入れ、採用すると、顧客契約を締結します。

図3:顧客契約
顧客契約には、次の特徴があります。
- オープンかつ不完全 顧客契約は、システムで使用可能なビジネス機能に関して、オープンかつ不完全です。 それらは、顧客のプロバイダ契約に対する期待という観点から、システムのビジネス機能能力のサブセットを表しています。
- 複数かつ権威がない 顧客契約は、サービスの顧客数に比例して複数存在し、プロバイダに課せられた契約上の義務の全体集合に関して、それぞれ権威がありません。 顧客からプロバイダへの関係の権威のない性質は、サービス指向アーキテクチャと分散アプリケーションアーキテクチャを区別する主要な特徴の1つです。 サービスコンシューマは、サービスコミュニティ内のピアが、自分とは全く異なる方法でプロバイダを利用する可能性があることを認識する必要があります。 ピアは異なる速度で進化し、システムの他の部分にある依存関係や期待を潜在的に混乱させる可能性のあるプロバイダの変更を要求する可能性があります。 顧客は、ピアがプロバイダ契約をいつどのように混乱させるかを予測できません。 分散アプリケーションのクライアントには、そのような懸念はありません。
- 限定的な安定性と不変性 プロバイダ契約と同様に、顧客契約は特定の期間および/または場所に有効です。
消費者主導契約
顧客契約により、プロバイダのライフサイクルの任意の時点で活用されているビジネス価値を反映することができます。 プロバイダ契約に対する期待を表し、主張することで、顧客契約は、そのプロバイダ契約のどの部分が現在、システムによって実現されるビジネス価値をサポートしており、どの部分がサポートしていないかを効果的に定義します。 これにより、サービスコミュニティは、まず顧客契約の観点から指定することによって、恩恵を受ける可能性があると示唆しています。 この見解では、プロバイダ契約は顧客の期待と要求を満たすために生まれます。 この新しい契約上の取り決めが派生的な性質であることを反映するために、そのようなプロバイダ契約を顧客主導契約または派生契約と呼びます。
顧客主導のプロバイダ契約の派生的性質は、サービスプロバイダと顧客の関係に異質な側面を追加します。 つまり、プロバイダは、その境界の外から発生する義務の対象となります。 これは、実装の本質的に自律的な性質に何ら影響を与えるものではありません。単に、サービスの成功は消費されることに依存しているという事実を明確にしているだけです。
顧客主導契約には、次の特徴があります。
- クローズドかつ完全 顧客主導契約は、既存の顧客によって要求されている機能の全体集合に関して、クローズドかつ完全です。 契約は、それらの期待が親アプリケーションに対して有効である期間中に、顧客の期待をサポートするために必要なエクスポート可能な要素の必須セットを表しています。
- 単一かつ権威がない プロバイダ契約は、システムで使用可能なビジネス機能の表現としては単一ですが、既存の顧客の期待の統合から派生するため、権威がありません。
- 限定的な安定性と不変性 顧客主導契約は、特定の顧客契約のセットに関して安定しており、不変です。 つまり、指定された顧客契約のセットに従って、顧客主導契約の有効性を判断でき、契約の前方および後方互換性の性質を時間と空間で効果的に制限します。 契約の互換性は、特定の顧客契約と期待については安定しており、不変ですが、期待の増減に応じて変更される可能性があります。
契約特性の要約
次の表は、この記事で説明されている3種類の契約の特徴をまとめたものです。
契約 | オープン | 完全 | 数 | 権限 | 限定的 |
---|---|---|---|---|---|
プロバイダ | クローズド | 完全 | 単一 | 権威がある | 空間/時間 |
顧客 | オープン | 不完全 | 複数 | 権威がない | 空間/時間 |
顧客主導 | クローズド | 完全 | 単一 | 権威がない | 顧客 |
実装
顧客主導契約パターンは、顧客契約と顧客主導契約を使用してサービスコミュニティを構築することを推奨しています。 ただし、このパターンでは、顧客契約と顧客主導契約が採用するべき形式や構造を指定しておらず、顧客の期待がプロバイダにどのように伝達され、プロバイダのライフサイクル中にどのように主張されるかも決定していません。
契約は、いくつかの方法で表現および構造化できます。 最も単純な形式では、顧客の期待はスプレッドシートや同様のドキュメントにキャプチャされ、プロバイダアプリケーションの設計、開発、テストフェーズで実装できます。 さらに進んで、各期待を主張する単体テストを導入することにより、各ビルドで契約が繰り返し可能な自動化された方法で記述および適用されるようにすることができます。 より高度な実装では、期待は、サービスエンドポイントの入力および出力パイプラインで実行時に評価されるSchematronまたはWS-Policyのようなアサーションとして表現できます。
契約の構造と同様に、プロバイダと顧客の間で期待を伝える際には、いくつかの選択肢があります。 顧客主導契約パターンは実装に依存しないため、適切な組織体制があれば、他のチームと話すだけで、または電子メールを使用して期待を伝えることができます。 期待の数や顧客数が多すぎてこの方法で管理できなくなった場合は、接続されたシステムのインフラストラクチャに契約サービスインターフェースと実装を導入することを検討できます。 いずれのメカニズムを使用する場合でも、通信はアウトオブバンドで行われ、システムのビジネス機能を実行する会話の前に実施される可能性が高いです。
メリット
顧客主導契約は、サービスの進化に関して2つの重要な利点を提供します。 まず、サービス機能の仕様と提供を主要なビジネス価値ドライバーを中心に据えています。 サービスは、消費される範囲においてのみビジネスにとって価値があります。 顧客主導契約は、エクスポート可能なサービスコミュニティ要素(顧客が仕事をするためにプロバイダから要求する要素)の価値を主張することにより、サービスの進化をビジネス価値に結び付けます。 その結果、プロバイダは、顧客を支えるビジネス目標と明確に連携したリーンな契約を公開します。 変更(サービスの進化)は、顧客が明確なニーズを表明する場合にのみ発生します。
もちろん、最小限のリーンな要件から始めて、顧客が要求に応じてサービスを進化させるには、サービスを制御された効率的な方法で進化、展開、運用できる状態にあることを前提としています。 これは、顧客主導契約パターンが2番目の重要な利点を提供する場所です。 顧客主導のプロバイダ契約により、変更を計画し、現在運用中のアプリケーションへの影響を評価するために必要な、きめ細かい洞察と迅速なフィードバックを得ることができます。 実際には、これにより、個々の顧客をターゲットにしたり、後方および/または前方互換性がない変更を行うのを妨げている期待を放棄するためのインセンティブを提供したりできます。 サービスプロバイダを顧客契約から派生させることで、システムライフサイクルの運用部分で使用できる知識のリポジトリとフィードバックメカニズムを付与します。
デメリット
この記事では、サービス環境に顧客主導契約を導入する動機を特定し、その後、顧客主導契約パターンがサービスの進化を決定する力をどのように対処するかについて説明しました。 最後に、パターンの適用範囲と、顧客契約と顧客主導契約の実装中に発生する可能性のある問題について説明します。
顧客主導契約パターンは、単一の企業またはよく知られたサービスの閉じたコミュニティのコンテキストで適用できます。 より具体的には、プロバイダが顧客が自分たちと契約を結ぶ方法に一定の影響力を及ぼすことができる環境です。 期待と義務を伝達および表現するためのメカニズムがどれだけ軽量であっても、プロバイダと顧客は、合意されたチャネルと規約を知り、受け入れ、採用する必要があります。 これにより、すでに複雑なサービスインフラストラクチャに、複雑さとプロトコル依存性のレイヤーが必然的に追加されます。
顧客主導契約に基づいて構築されたシステムは、契約に対する破壊的な変更をより適切に管理できると示唆してきました。 しかし、このパターンが破壊的な変更の問題に対する万能薬であると示唆するつもりはありません。結局のところ、破壊的な変更は依然として破壊的な変更です。 しかし、このパターンは、実際に破壊的な変更を構成するものの多くの洞察を提供すると考えており、そのため、サービスのバージョン管理戦略の基礎として機能する可能性があります。 さらに、すでに説明したように、パターンを実装するサービスコミュニティは、サービスの進化の影響をより適切に予測できます。 特に、プロバイダの開発および運用チームは、進化戦略をより効果的に計画できます。たとえば、特定の期間に契約要素を非推奨にし、同時に、契約の新しいバージョンにアップグレードするためのインセンティブを、抵抗している顧客に提供することによってです。
コンシューマ駆動契約は、サービス間の結合を必ずしも削減するわけではありません。疎結合サービスはお互いに比較的独立していますが、それでも結合されたままです。しかし、このパターンが行うのは、そのような残存する「隠れた」結合を掘り起こして明らかにすることによって、プロバイダとコンシューマがそれらをより良く交渉し、管理できるようにすることです。
コンシューマとコンシューマ駆動契約がどのようにビジネス価値を表現するかについて、議論してきました。しかし、そのような契約をビジネス価値の指標または尺度とは見なさないことを明確にしておく必要があります。それらはビジネス指標ではありません。そして、WS-AgreementやWSLAなどの仕様との表面的な類似性にもかかわらず、それらはサービスレベルアグリーメント(SLA)を表すことを意図していません。ここでの根本的な仮定は、サービスそれ自体はビジネスにとって価値がなく、その価値は消費されることにあるということです。サービスを消費者が使用する場所に近づけて指定することにより、リーンでジャストインタイムな方法でビジネス価値を活用することを目指しています。
最後に、コンシューマ契約によってサービスプロバイダの仕様を駆動することを許可すると、そのプロバイダの概念的整合性が損なわれるリスクがあることを指摘しておく必要があります。サービスは、個別に識別可能な、再利用可能なビジネス機能をカプセル化しており、その整合性は、その権限範囲外の不合理な要求によって損なわれるべきではありません。
さらに聞く
2006年3月のMicrosoft Architect Insight Conferenceで、MicrosoftのRon Jacobsが私とMartin Fowlerへのインタビューを行っています。変化するアーキテクチャの文脈で、コンシューマ駆動契約について議論しています。
謝辞
Ian Cartwright、Duncan Cragg、Martin Fowler、Robin Shorrock、Joe Walnes
重要な改訂
2006年6月12日: 初版公開