コンピューティング・システムのコンポーネントを統合する最近の手法として、SOA(Service-Oriented Architecture、 参考文献 を見てください)の価値は広く認識されています。Webサービスは、こうしたシステム・コンポーネントの間のコミュニケーションに対して、共通の、オープン・スタンダードに基づくベースとなります。Webサービスのプロトコルは、企業間での統合にも企業内の統合にも適しています。SOAの重要な目的として、コンポーネント同士を疎結合とすることが挙げられます。コンポーネント同士が疎結合であれば、他のコンポーネントに影響を与えることなくコンポーネントの振る舞いの一部を変更することができます。これが重要な目的であることは確かですが、現実には、ある1つのコンポーネント内の変更によって他のコンポーネントが影響を受ける場合が数多くあるものです。
この記事では、Webサービスのコンシューマーに影響を与える変更のタイプと影響を与えない変更のタイプを識別するためのプロセスについて説明します。そして、実際にサービス・コンシューマーに影響を与える変更を処理する場合の戦略と、最も適切なソリューションを選択するための判断基準について説明します。判断基準として、ソリューションを選択するためのコストは重要であり、あるソリューションを特定なビジネス・シナリオに適用する前に、その利点とコストを注意深く評価する必要があります。
この記事では、Rational ® Application Developerの使い方の詳細に大きな部分を割いています。ここで説明する方法によって、Webサービスへの変更を実装する場合にサービス・プロバイダーとサービス・コンシューマー双方への影響を減少させることができます。
現在のWebサービス標準は、変更の管理に関して、ほとんど何も、あるいはまったくサポートしていません。そこでこの記事では、Webサービスの変更管理のためのベスト・プラクティスを示そうと思います。
『後方互換性』とは、以前の世代のシステムによるインターフェースやデータを使用できるハードウェアあるいはソフトウェア・システムを意味します。Webサービスという意味合いでの後方互換性は、あるインターフェースへの変更が、そのインターフェースを使用している既存ユーザー(サービス・コンシューマーも言われます)にどのような影響を与えるかを言います。もし既存ユーザーが影響を受けなければ、その変更には後方互換性があります。もし既存ユーザーが影響を受けるのであれば、その変更には後方互換性がなく、変更の影響を管理するための戦略が必要になります。
後方互換性の概念は、『変更』と『変更管理』の概念に密接に関連しています。どのようにWebサービスを設計したとしても、インターフェースに変更が必要となる状況が生じるものです。この変更は、計画的かつシステム的に行う必要があり、また充分に準備して行う必要があります。通常、Webサービスに対する変更管理には、次のようなステージがあります。
- その変更が後方互換かどうかを評価する
- 後方互換でなければ、古いインターフェースをサポートすることが必要かどうかを評価する
- もし必要であれば、古いインターフェースと新しいインターフェースの両方を同時にサポートできるように変更を設計し、実装する
- 事前定義された猶予期間後、古いインターフェースを廃止する
次のセクションでは、第1ステージ、つまり後方互換性の評価について検証します。
第2ステージは、ビジネス管理の領域です。たとえ猶予期間があったとしても、新しいサービスへの切り換えや古いサービスの廃止を厳格に行ってしまうと、ビジネスに深刻な中断が生ずるかも知れません。しかしこの判断プロセスはビジネス管理の領域であるため、主に開発チームを対象としたこの記事では対象の範囲外です。
この記事のこれから先では、古いインターフェースと新しいインターフェースの両方を同時にサポートするためのバージョン管理手法(第3ステージ)と、既存インターフェースを廃止するための手法(第4ステージ)を紹介します。この、最後のステージに関する説明では、『デプリケーション(deprecation:使用すべきではない)』という概念を使います。Webサービスは、一定期間後に廃止(decommissioned)されると、「使用すべきでない(deprecated)」と宣言されるのです。
後方互換性を持つ変更と持たない変更の分類に関しては、幾つかの論文が発表されています( 参考文献 を見てください)。このセクションの目的は、これまでに行われた議論を確認し、補完することです。
一部の変更は、既存のインターフェースを使用するサービス・コンシューマーにとって完全に透明な形で行うことができます。一例として、次のサンプル・インターフェースと、起こり得る変更について考えてみましょう。
- WebサービスのURL:
- http://bigbank.com/BigBank/services/AccountService
- オペレーションは下記のように定義されています(output_message operation(input_message) という表記を使っています)。
- accountNumber create(name, street, suburb, postcode, state)
- amount deposit(accountNumber, amount)
- amount withdraw(accountNumber, amount)
- amount balance(accountNumber)
既存ユーザーに影響を与えずに行える変更が、幾つもあります。例えば次のような変更です。
-
新しいオペレーションを追加する
- 新しいオペレーションが新しいコンシューマーに対して利用可能となっても、既存のコンシューマーは相変わらず既存のオペレーションを呼び出すことができます。既存のコンシューマーは影響を受けません。
-
例: transferというオペレーションの追加は、後方互換性を持つ変更です。
- amount transfer(fromAccountNumber, toAccountNumber, amount)
-
入力メッセージに対して、新たな、オプションのデータ構造を追加する
- 既存のコンシューマーは新しいデータ構造のことを知らないため、影響を受けません。
-
例:
次のように、createオペレーションにcountryというオプション・フィールドを追加する
- accountNumber create(name, street, suburb, postcode, state, country=null)
-
既存の入力データ構造のカーディナリティー(cardinality)を必須(mandatory)からオプション(optional)に変更する
- 既存のコンシューマーは、あたかも入力データ構造が相変わらず必須であるかのように使い続けることができます。既存のコンシューマーは影響を受けません。
-
インターフェースに対して何ら実質的な影響を与えないような、サービス・プロバイダー実装への変更
- サービス実装は、インターフェースに実質的な影響がない限り、変更することもできれば、完全に書き直すこともできます。つまり、既存のインターフェース仕様は完全にサポートされています。
次のような変更を行うと、既存のコンシューマーが即座に動作不能になります。
-
オペレーションの削除
- 古いインターフェースは、もはや新しいインターフェースのサブセットではなくなります。削除されたオペレーションを使っている既存コンシューマーは、影響を受けます。
- 例: amount transfer(fromAccountNumber, toAccountNumber, amount) というオペレーションを削除すると、そのオペレーションを使っているコンシューマーは動作できなくなります。この変更は後方互換ではありません。
-
既存の出力データ構造のカーディナリティーを変更する
- 出力メッセージの中にあるフィールドのカーディナリティーを変更すると(例えば、必須フィールドをオプション・フィールドに変更する、など)、既存のコンシューマーは動作できなくなります。
-
データ型の定義への変更
- 入力メッセージ、または出力メッセージのデータ型に対する変更は、大部分が後方互換性を持ちません。
- 例: データを十進データ型から浮動小数点型に変更するような単純な変更であっても、既存のコンシューマーは動作できなくなります。
-
ビジネス・ルールあるいはビジネス・プロセスへの変更
- 場合によると、Webサービスのオペレーション同士の間に依存関係があります。通常、この依存関係は、一連のオペレーションを処方通りの順序で呼び出す必要があるために発生します。基礎となるビジネス・ルールあるいはビジネス・プロセスが変更されると、依存関係も変化するため、たとえインターフェースが変更されなくても(あるいは後方互換性を保って変更されたとしても)もはや既存のクライアントは正しく動作できなくなります。
- 例: Big Bankという銀行が、これまで引き出しが許可されていた場合にのみ引き出しを実行する、と決定したとしましょう。そうすると当然、boolean authorize(accountNumber, amount) のような新しいオペレーションを導入する必要があります。ここまでの説明では、この変更は後方互換性を持っています。しかしこの場合の変更は後方互換ではありません。withdrawというオペレーションを使用する既存のコンシューマーはauthorizeを呼び出すことはないため、動作できなくなるのです。
変更の中には、既存のコンシューマーには影響を与えないものの、不利な結果をもたらす可能性のあるものもあります。サービス・プロバーダーは、こうしたカテゴリーの変更それぞれに対して、後方互換性を考慮しながらケースバイケースで評価する必要があります。
-
QoS(quality of service)の変更に対する考慮
- サービス・プロバーダーが、インターフェースには影響を与えないもののQoS(レスポンス時間や可用性など)を悪化させるような変更を実装する際には、その変更は既存のコンシューマーに悪い影響を与えます。
- コンシューマーは、(レスポンス・メッセージの中の)あるフィールドの値の範囲に関して、何らかの想定をしているかも知れません。もしプロバイダーが、想定される範囲外の値を返し始めると、既存のコンシューマーに対して悪い影響を与える可能性があります。
- 例: AccountNumberは、<xsd:pattern value="[0-9]{12}"/> に制限されています(つまり口座番号は厳密に12桁の文字列です)。先頭にゼロがあることは許されていますが、オリジナルの実装では先頭にあるゼロを返しません。この実装を変更し、先頭にゼロを持つ口座番号が作られる可能性が出てくると、一部のコンシューマーで問題が起きるかも知れません。
後方互換性を持たない変更をサービスに加える場合、古いバージョンのサービスも新しいバージョンのサービスも共に利用可能にしておく必要があるのであれば、両方のインターフェースをサポートするための方法が必要です。
ここで説明するバージョン管理方法では、既存インターフェースを削除することなく、新しいインターフェースをWebサービスに導入することができます。それぞれの方法が与える影響は同じではないため、シナリオによって、どの方法が適切かは異なります。どのバージョン管理方法でも、異なるバージョン間で型に関する混乱が起きるのを避けるため、スキーマ・タイプの名前空間を変更した方が得策です。
サービス内のオペレーションに対して、既存のコンシューマーでは動作しない新しいバージョンを導入する場合には、オペレーションによるバージョン管理を考えます。古いオペレーションに基づいて新しいオペレーションを作成し、両方のオペレーションをインターフェース中に保持しておけば、後方互換性を保つことができます。前のバージョンのオペレーションは、利用可能なままにしておきます。これによって変更は後方互換となり、古いバージョンと新しいバージョンの両方のオペレーションを共存させることができます。
例えば、Account.createオペレーションがcountryを必須入力として受け付けるためには、Account.createオペレーションに変更が必要かも知れません。この変更を既存オペレーションに影響を与えずに行うためには、次のような新しいオペレーションを追加します。
- Account.createV2(name, street, suburb, postcode, state, country)
オリジナルのcreateオペレーションの実装を変更せずにおくことによって、古いバージョンのオペレーションも新しいバージョンのオペレーションも、両方利用することができます。
このバージョン管理方法は実装が容易であり、バージョン変更の影響を単一のオペレーションに限定することができますが、幾つか欠点も持っています。どちらのオペレーションも同じWebサービス内に実装する必要があるため、Webサービスは一層緊密に実装コードに結合され、このオペレーションを将来削除しようとすると、非常に面倒なことになります。そして、さらにバージョンが追加されると、同じWebサービスの中に膨大な数のオペレーションがあることになりかねません。また、ある1つのWebサービスの複数のオペレーションに影響を与える変更(複数のオペレーションで共有されるデータ型への変更など)が必要になると、オペレーションによるバージョン管理では、すぐに身動きが取れなくなります。
Webサービスの名前によるバージョン管理は、1つのオペレーションではなくWebサービス全体に影響を与えるため、オペレーションによるバージョン管理よりも大きな影響を持っています。
例えば、AccountServiceという、ある既存のWebサービスがある場合、別の名前(AccountServiceV2など)を持つ新しいサービスを定義することができます。そうすると、既存のAccountServiceというWebサービスに影響を与えることなく、AccountServiceV2でのオペレーションに対して、新しい、あるいは変更された機能を実装することができます。変更されていないオペレーションもAccountServiceV2で実装できるため、古いAccountServiceは、必要が無くれば削除することができます。
別バージョンとして新しいWebサービスを導入すると、変更がオリジナルの実装から分離される、という利点もあります。もしある時点で前のバージョンを削除する必要が出てきた場合には、オペレーション毎にコードを操作するのではなく、そのWebサービス全体を削除することができます。
ただし欠点として、Webサービスの開発に使用されるツールキットによっては、別バージョンとしてのWebサービス名が、生成される実装コードの中に入ってしまうかも知れません。これはつまり、新しいバージョン毎に、別々の名前が付いた一連のオブジェクトが作成されることを意味します。そのためコンシューマーにとっては、既存コードの中に統合するために余分な作業が必要になるかも知れません。
URL、またはエンドポイントによってバージョン管理を行う場合には、オペレーションやWebサービスの名前ではなく、エンドポイントのURLを変更します。
例えば、既存のWebサービスが次のURLにある場合、
- http://bigbank.com/BigBank/services/AccountService
新しいバージョンは、既存のWebサービスに影響を与えることなく、次のURLに置くことができます
- http://bigbank.com/BigBank1/services/AccountService
この新しいバージョンには、必要な変更が行われた一連のオペレーションがすべて含まれています。そのためコンシューマーは、このWebサービスにアクセスするために、どちらか一方のURLのみを使用することができます。このバージョン管理方法では、Webサービスとオペレーションの名前は同じままなので、新しいバージョンを利用できるようにコンシューマーをアップグレードするために必要な変更の量は、これまで紹介した2つの方法よりも少なくて済みます。また、生成されるコードに対する影響も、他の方法よりも少なくて済みます。
変更はコンテキスト・ルートで起こるため、この場合も、新しいインターフェースは既存のインターフェースとは独立です。ただし、新しいURLに対するリクエストを処理するために、別のWebモジュールを作成する必要があります。オペレーションやWebサービス名によるバージョン管理では、別のWebモジュールは必要ありません。
同じサービスに対して複数のバージョンを維持することは、高くつく可能性があります。表1は、後方互換性を持った変更を行う場合と、後方非互換の変更をバージョン管理を使用して行う場合のコストを比較しています。
表1. 後方互換の変更と、後方非互換でバージョン管理を使用する変更のコスト比較
| コスト | ||
|---|---|---|
| サービス・プロバーダーのアクティビティー | 後方互換の変更 | バージョン管理によって影響を緩和した、後方非互換の変更 |
| 設計と開発 | どちらのシナリオでも同様。コストは変更の複雑さに依存する。 | |
| 新しいインターフェースに対するプログレッション(progression)テスト | どちらのシナリオでも同様。 | |
| 古いインターフェースに対するリグレッション(regression)テスト | 小さい | 無し |
| 漸進的な実装 | 無し | 追加コストが多少必要(通常、バージョンが複数あると、余分なデプロイ作業が必要となる) |
| 維持管理(例えば欠陥の発見と修復など) | 低い(欠陥は一度修復すればよい) | 高い(2つ以上のバージョンで欠陥を修復する必要がある) |
| 古いインターフェースの廃止 | 無し | 小さい(古いバージョンは無くなる) |
| サービス・コンシューマーのアクティビティー | ||
| 新しいインターフェースを採用するための設計と開発 | 無し | 追加コストが多少必要(変更の複雑さに依存する) |
| 新しいインターフェースのプログレッション・テスト | 無し | |
| デプロイメント | 無し | 追加コストが多少必要 |
上記の表は、バージョン管理を行う後方非互換の変更が、後方互換の変更よりもコストが高いことを示しています。しかもそのコストは、サポートすべきバージョン数の増加に比例して増加します。
一方、バージョン管理を行わない後方非互換の変更が導入されると、既存のコンシューマーは動作できなくなります。そのサービスに依存する組織は、ビジネスの中断や、潜在的な収入の喪失、そして/あるいはペナルティーに直面することになります。
当然のことですが、古いインターフェースをまったくサポートしない場合と、無限にサポートすることとを適切にバランスさせる必要があります。そのために推奨される方法としては、次のようなものがあります。
- 新しいバージョンのサービスが利用可能となったら、一定期間後に廃止することを示すために、古いバージョンを「使用すべきではない」と宣言する。
- コンシューマーが、使用しているコードを変更してテストする時間を確保できるように、サービスを「使用すべきでない」とコンシューマーに通知する。古いバージョンのサービスの廃止には、適切な通知期間(通常は3ヶ月以上)が必要です。
- 任意の時間において、1つのサービスに対して2つ以上のバージョンは動作できないようにする。2つ以上のバージョンをサポートすることはサービス・プロバーダーにとって高価であるばかりではなく、そもそも変更の理由であった、ビジネス的な利点の実現も遅らせてしまいます。
- 2つのバージョンが利用可能である間に、両方のバージョンを切り換えて使う。
どのバージョン管理方法でも、1つ以上のWebサービスに対して複数の定義を同時にサポートする方法が用意されています。理論的には、これまでに説明したどの方法を使っても、ある1つのWebサービスに対して幾らでも多くのバージョンをサポートすることができます。しかし一般的には、先ほど述べた理由から、同じサービスに対して同時に維持管理するバージョンは、2つにとどめるべきです。
ある1つのサービスの2つのインスタンスを管理する場合には、新しいバージョンを導入している間に2つのバージョンを切り換えて使うのが得策です。切り換えには、URLによるバージョン管理を使います。
例えば、同じサービスに対する2つのバージョンを考えてみましょう。現在のバージョンは、すべて次のベースURLの下にあるとします。
- http://bigbank.com/BigBank/services/
そしてこの下に、次のような一連のWebサービスがあるとします。
- http://bigbank.com/BigBank/services/AccountService
- http://bigbank.com/BigBank/services/CustomerService
- http://bigbank.com/BigBank/services/EmployeeService
もしリリースの中でAccountServiceの新しいバージョンが必要だとすると、URLによるバージョン管理を使う場合には次のようなサービスもあるかも知れません。
- http://bigbank.com/BigBank1/services/AccountService
しかし前のバージョンのWebサービスも保持されています。そのため、両方のバージョンのWebサービス一式に対するURLは次のようになります。
- http://bigbank.com/BigBank/services/AccountService <- 古いバージョン
- http://bigbank.com/BigBank/services/CustomerService
- http://bigbank.com/BigBank/services/EmployeeService
- http://bigbank.com/BigBank1/services/AccountService <- 新しいバージョン
オリジナルのサービスは、相変わらずオリジナルのURLで利用可能です。そして新しいサービスは、新しいURLで利用可能です。
ここまでは、単純にURLによってバージョン管理する場合と同じです。つまり新しいバージョンのWebサービスが導入され、そして新しいURLが作成されます。しかしここで、さらに変更が行う別のリリースを考えてみましょう。AccountServiceに対して別の変更を加え、またCustomerServiceにも変更を加えるのだとします。切り換えを使うと、新しい2番目のバージョンのAccountServiceに対して新しいURLを導入する代わりに、切り換えてオリジナルのURLに戻ることができます。次のリリース後の、新しいWebサービス一式に対するURLは次のようになります。
- http://bigbank.com/BigBank/services/AccountService <- 新しいバージョン
- http://bigbank.com/BigBank/services/CustomerService <- 古いバージョン
- http://bigbank.com/BigBank/services/EmployeeService
- http://bigbank.com/BigBank1/services/AccountService <- 古いバージョン
- http://bigbank.com/BigBank1/services/CustomerService <- 新しいバージョン
必要なバージョンはいつでも2つのみのため、オリジナルのURLを再使用することができ、必要なWebモジュールを制限することができます。そのため、ベースURL(コンテキスト・ルート)やバージョン番号で一杯になることはなく、既存のバージョンは、すべて2つのURLの中に保持されます。
上に示した例では、1つのWebサービスの、2つのバージョンをサポートすることができます。例外的な状況(規制によって強制される変更や、バグ修正のための変更)では、この概念を拡張し、一連の有効なコンテキスト・ルートを拡張して任意の数のバージョン(BigBank2、BigBank3など)を併存させることができます。また、コンテキスト・ルートの命名規則として、他の規則も使うこともできます(BigBankAやBigBankBなど)。
当然ながらこの手法では、ある特定なWebサービスの最新バージョンが、どのコンテキスト・ルートに含まれているのかをサービス・コンシューマーに通知する必要があります。通常、こうした詳細はリリース・ノートの中に記述されます。またこの手法では、古いWebサービスが利用できる期間が制限されるため、Webサービスのコンシューマーは次のリリースの前に最新バージョンにアップグレードするように強制されます。
この記事で使用しているBig Bankの例では、URLによるバージョン管理と、切り換えを使いました。この方法では、新しいインターフェースの実装を既存のインターフェースから分離することができ、また必要なWebモジュールの数を削減できるという利点もあります。次のセクションでは、実装例の詳細を説明します。
この先のセクションでは、URLによるバージョン管理の実装方法を説明します。ここで使用する例は、先に取り上げた、AccountServiceというWebサービスです。下記のWSDLファイルとXSDファイルは、AccountServiceというWebサービスに対するインターフェースを記述しています。
- Types.xsd
- Account.xsd
- BankFault.xsd
- AccountService.wsdl
Types.xsdは、AddressとDollarAmountそしてAccountNumberに関する下位レベルの型定義を含んでいます。Account.xsdは、AccountServiceというWebサービスのオペレーションに対するリクエストとレスポンスのスキーマ定義を含んでいます。BankFault.xsdは、すべてのオペレーションに対する例外型を含み、AccountService.wsdlはWebサービス定義を含んでいます。
Big Bankは他の国にもサービスを拡大したいと思っていますが、Account.createオペレーションに対するWebサービス定義は、リクエスト中のAddress型の一部としてcountryを許すようになっていません。またBig Bankは、既存のWebサービス・クライアントがアプリケーションを新しいWebサービス定義に移行できるように、後方互換性を確保できる期間も設けたいと思っています。そのため、既存のWebサービス定義も並行してサポートする必要があります。
この例では、IBM Rational Application Developer Version 6.0.1を使って、既存のサービスを変えずに維持したまま新しいWebサービスを作成します。
Account.createというWebサービス・オペレーションには、countryに対する新しい必須パラメーターが必要です。そこで、この新しいフィールドを追加するためにAddress型を変更します。また、既存の定義との衝突を避けるために、そして新しいバージョンと古いバージョンのAddressの間で混乱が生じないように、この型を置くための新しい名前空間も作成します。year and monthは名前空間のバージョン管理のデファクト標準であるため、この名前空間にはyear and monthも追加します。
新しいバージョンのTypes.xsdを作成します。変更部分は、 リスト1 の中の 太字 でハイライトされています。
リスト1. 国名(country name)をサポートする、新しいバージョンのTypes.xsd
<?xml version = "1.0" encoding = "UTF-8"?>
<xsd:schema attributeFormDefault="qualified"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://parts.bigbank.com/2006/03"
targetNamespace="http://parts.bigbank.com/2006/03">
<xsd:complexType name="Address">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="street" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="1" name="suburb" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="1" name="postcode" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="0" name="state" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="1" name="country" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="DollarAmount">
<xsd:restriction base="xsd:float">
<xsd:pattern value="[0-9].[0-9]{2}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="AccountNumber">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[0-9]{12}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
|
今度は、このバージョンのTypes.xsdを使うために、新しいバージョンのAccount.xsdを作成する必要があります。そこでAccount.xsdファイルの中の名前空間を変更し、すべての型定義は、このファイルの中にある通りにします。こうする理由は、リクエスト・メッセージとレスポンス・メッセージはWebサービスに専用なものであり、ここではWebサービス全体に対する新バージョンを作ろうとしているためです。ですから、すべてのリクエスト・メッセージとレスポンス・メッセージに対して新バージョンを作ることは意味があるのです。
リスト2 はAccount.xsdファイルの断片であり、変更部分は 太字 でハイライトされています。
リスト2. country nameをサポートする、新しいバージョンのAccount.xsd
...
<xsd:schema attributeFormDefault="qualified"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://parts.bigbank.com/2006/03"
targetNamespace="http://parts.bigbank.com/2006/03">
<xsd:include schemaLocation="Types.xsd"/>
...
|
最後に、新しいバージョンのWSDLファイルを作ります。Webサービスに対する名前空間を変更して日付のバージョンを持つようにし、またエンドポイントも変更します。このエンドポイントは、新しいバージョンのWebサービスにアクセスするために私達のWebサービスのコンシューマーが使用するものです。 リスト3 とリ スト4 は、WSDLに対する変更を示す2つの断片です。ここでも変更部分は 太字 でハイライトされています。
リスト3. 新しいバージョンのAccountService.wsdl(名前空間に対する変更)
...
<wsdl:definitions name="AccountService"
targetNamespace="http://account.services.bigbank.com/2006/03"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://account.services.bigbank.com/2006/03"
xmlns:intf="http://account.services.bigbank.com/2006/03"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns1="http://parts.bigbank.com/2006/03"
xmlns:tnsf="http://faults.bigbank.com/2006/03"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://faults.bigbank.com/2006/03" schemaLocation="BankFault.xsd"/>
<import namespace="http://parts.bigbank.com/2006/03" schemaLocation="Account.xsd"/>
</xsd:schema>
</wsdl:types>
...
|
リスト4. 新しいバージョンのAccountService.wsdl(エンドポイントに対する変更)
...
<wsdl:service name="AccountService">
<wsdl:port binding="impl:AccountSoapBinding" name="AccountService">
<wsdlsoap:address location="http://bigbank.com/BigBank1/services/AccountService"/>
</wsdl:port>
</wsdl:service>
...
|
これで新しいバージョンのWSDLは完了したので、古いバージョンと新しいバージョンのWebサービスが併存できるように、アプリケーションのパッケージ方法を決める必要があります。ここで仮に、既存のバージョンを 図1 のようにパッケージしたとしましょう。
図1. 既存バージョンのBig Bankサンプル・アプリケーションをパッケージする
BigBankClientは、Big BankのWebサービスを使用するサードパーティーのクライアントです。BigBankWebコンポーネントは生成されたWebサービス・コードを含んでおり、そのコンテキスト・ルートはBigBankに設定されています。そして最後に、BigBankEJBコンポーネントは、AccountServiceというWebサービスに対するビジネス・ロジックを含んだAccount EJBを持っています。
新しいバージョンのAccountService Webサービスにアクセスするためのエンドポイントには新しいコンテキスト・ルートがあるため、BigBank1というコンテキスト・ルートを持つ2番目のWebプロジェクトを作成する必要があります。この新しいWebプロジェクトは、このWebサービスに対する新しいビジネス・ロジックを実装した、既存のEJBの新しい機能を呼びます。そうすると、新しいパッケージは、 図2 のようになります。
図2. 新しいバージョンのBig Bankサンプル・アプリケーションをパッケージする
新しいWebサービス・プロバイダー・コンポーネントを生成する
上位レベルの設計が終わり、WSDLファイルとXSDファイルに必要な変更を加えてしまえば、新しいWebサービス・プロバイダー・コンポーネントの生成は比較的単純です。
通常、Rational Application DeveloperのWebサービス・ウィザードは、Webサービスのデプロイメント・モジュールの中に置かれたWSDLファイルとXSDファイルに対してマイナーな変更を加えます。そのため、こうしたファイルを別のプロジェクトの中に置いておくことは良い考えです。 図3 に示すように、アップデートされたWSDLファイルとXSDファイルは、Build/wsdl/schema200603の下のBuildという名前のプロジェクトに中に、オリジナルのファイルと並べて置かれています。
図3. Rational Application DeveloperのProject Explorerが、Buildプロジェクトのコンポーネントを示している
先ほど説明した通り、新しいバージョンのWebサービスをホストするために、BigBank1というコンテキスト・ルートを持ったWebモジュールが必要です。この場合では、このWebサービスにバージョンを付けるのはこれが初めてなので、必要なコンテキスト・ルートを持ったBigBank1Webという名前の新しいWebモジュールを作ります。次回、このWebサービスにバージョンを付けた後は、オリジナルのコンテキスト・ルート(BigBank)に切り換えて戻ることができます。その場合、同様のプロセスを使って、BigBankWebの中にWebサービスのプロバイダー・コンポーネントを生成することができます。
Webサービスのプロバイダー・コンポーネントを作成するためには、Build/wsdl/schema200603/AccountService.wsdlのWSDLファイルを選択し、『New Web Service』ウィザードを呼び出します。ここでは、BigBank1Webプロジェクトの中にあるWSDLファイルから、スケルトンJava bean Webサービスを生成しました。『Define custom mapping for namespace to package』というウィザード・オプションを使うのは良い習慣です。ここではAccount-NStoPkg.propertiesというマッピング・ファイルを作成し、year and month名前空間をJavaパッケージ名に含めるようにしました。この方法によって、異なるバージョンの名前空間から生成されたコード間での混乱を避けることができます。 図4 は、生成されたJavaパッケージを示しています。
図4. Rational Application Developerによって生成されたJavaパッケージ
生成されたWebサービス・スケルトンからビジネス・ロジックを呼び出す
AccountSoapPort.javaは生成されたサービス・エンドポイント・インターフェースであり、AccountSoapBindingImpl.javaは生成されたJava bean Webサービス・スケルトンです。Account EJBの中で必要なビジネス・ロジックを呼び出すためのコードを、この生成されたスケルトンに追加する必要があります。ここではAccount.createメソッドの中に1つパラメーターを追加しただけなので、新しいAccountSoapBindingImpl.javaと古いバージョンとの違いは僅かです。生成されたすべてのクラスに対してインポートを定義すると、AccountSoapBindingImpl.javaのインポート・セクションに対する(名前空間の変更による)パッケージ名の変更を分離することができます( リスト5 )。
リスト5. 新しいバージョンのAccountSoapBindingImpl.java(名前空間の変更)
...
import com.bigbank.faults_2006_03.BankException;
import com.bigbank.parts_2006_03.ACreateRequest;
import com.bigbank.parts_2006_03.ACreateResponse;
import com.bigbank.parts_2006_03.AWithdrawRequest;
import com.bigbank.parts_2006_03.AWithdrawResponse;
import com.bigbank.parts_2006_03.ADepositRequest;
import com.bigbank.parts_2006_03.ADepositResponse;
import com.bigbank.parts_2006_03.ABalanceRequest;
import com.bigbank.parts_2006_03.ABalanceResponse;
...
|
もう一方の変更は、Webサービス・リクエストにcountryフィールドを含めるように、Account.createへのコールをアップデートすることです( リスト6 )。
リスト6. 新しいバージョンのAccountSoapBindingImpl.java(Account.createの変更)
...
public ACreateResponse create(ACreateRequest request)
throws java.rmi.RemoteException, BankException {
AccountLocal account = createAccountLocal();
ACreateResponse response = new ACreateResponse();
response.setAccountNumber(account.create(
request.getCustomerName(),
request.getAddress().getStreet(),
request.getAddress().getSuburb(),
request.getAddress().getState(),
request.getAddress().getPostcode(),
request.getAddress().getCountry()));
return response;
}
...
|
ここでは、Rational Application DeveloperのSnippetsビューを使ってAccount EJBのローカル・インターフェースへのコールを生成しました。『Call an EJB create method』という断片によって、EJBの呼び出しが非常に容易になります。
当然ながら、新しいWebサービスに必要なビジネス・ロジックの変更も実装する必要があります。先ほど説明した通り、ビジネス・ロジックの変更は、新しいcreateメソッドをAccount EJBに追加することで実装しました(このcreateメソッドがcountryに対する追加のパラメーターを受け付け、必要な処理を実行します)。こうすることによって、古いcreateメソッドは古いWebサービスで相変わらず利用可能であり、古いWebサービスも新しいWebサービスも、変更されていないビジネス・ロジックをアクセスする場合には同じAccount EJBメソッドを利用することができます。実装のための作業はインターフェースの変更とビジネス・ロジックの変更が中心となり、変更されていないビジネス・ロジックを最大限に再利用することができます。
これで、サービス・コンシューマーはAccountServiceというWebサービスの2つのバージョンを同時に利用することができます。先ほどの説明の通り、古いバージョンのAccountServiceは相変わらず変更なく利用可能ですが、サービス・コンシューマーには、古いバージョンは使用すべきでないこと、そして一定期間後(通常は次回の予定リリース時)に廃止される、ということが通知されています。サービス・コンシューマーは、古いバージョンが廃止される前に、新しいバージョンのAccountServiceを使うように自分たちのWebサービス・クライアント・アプリケーションをアップデートする必要があります。ここで、新しいバージョンのAccountServiceが使えるようにクライアント・アプリケーションをアップデートするために、サービス・コンシューマーは何をすべきか、少し見ることにしましょう。
新しいバージョンのWebサービスが使えるようにクライアント・アプリケーションをアップデートする
私達のシナリオでは、現在の多くのWebサービス実装と同様、UDDIは使っていません。通常、こうした状況では、サービス・コンシューマーが新しいWSDLファイルとXSDファイルにアクセスできるように、サービス・プロバイダーはWebサイトへのリンクを提供します。Rational Application Developerのテスト環境の中で次のリンクを使うと、私達のWebサービスに対する新しいWSDLとXSDにアクセスすることができます。
- http://localhost:9080/BigBank1/services/AccountService/wsdl.
私達のシナリオでは、単純にRational Application DeveloperのWeb Service Clientウィザードを使って、古いWebサービス用のTest ClientアプリケーションをBigBankClientWeb モジュールの中に作りました。このモジュールをアップデートして新しいバージョンのAccountServiceを使用できるようにするためには、まずBigBankClientWebに対するWebデプロイメント記述子を開き、古いAccountServiceに対するServiceRefを削除します( 図5 )。
図5. BigBankClientWebモジュールに対するWebデプロイメント記述子
次に、古いWebサービスのコンシューマー用に生成されたコンポーネントを削除する必要があります。これらのコンポーネントに含まれるものとしては、古いWSDLファイルとXSDファイル、Webサービスのクライアント・バインディング・ファイル、生成されたWebサービス・プロキシー・クラスなどがあります( 図6 )
図6. Webサービスのコンシューマーから削除すべきコンポーネント
先ほど、Rational Application Developerを使用するサービス・プロバイダーに関して触れた通り、サービス・コンシューマーは、(ビルド時に使用した)ダウンロードしたWSDLファイルとXSDファイルを保持するために、別のプロジェクトを作成することも考慮する必要があります。ここでは、Build/wsdl/schema200603/AccountService.wsdlで『New Web service client』ウィザードを呼び出し、BigBankClientWebプロジェクトの中にJavaプロキシーを生成しました。サービス・プロバイダーに関しては、『Define custom mapping for namespace to package』ウィザード・オプションを使って、Javaパッケージ名の中にyear and month名前空間を含めるようにします。
最後のステップは、生成された新しいWebサーバー・プロキシーを使用するようにクライアント・アプリケーションを変更することです。そのためには、サービス・コンシューマーの場合と同様、クライアント・アプリケーションを変更し、Webサービスのプロキシー・クラスに対して、名前空間の変更によって生まれた新しいパッケージ名を使うようにします。また、クライアント・アプリケーションには、新しいインターフェースを利用するための変更も必要です。私達のシナリオでは、クライアント・アプリケーションには、AccountService.createに対するリクエスト・メッセージを構成する際にcountryを含めるようにする変更が必要です。
「使用すべきでない」とする経過期間が過ぎた後は、Webサービスのバージョン管理プロセスの最終ステージを完了することができます。使用すべきでなくなったWebサービス・インターフェースを廃止、あるいは削除するためには、幾らでも方法があります。Webサービスを使用不可にするために比較的容易な方法は、Webデプロイメント記述子(BigBankWeb/WebContent/WEB-INF/web.xml)の中のサーブレット定義を単純にコメントアウトしてしまう方法です( リスト7 )。
リスト7. コメントアウトされた、Webサービスのサーブレット定義
...
<!-- <servlet>
<servlet-name>com_bigbank_services_account_AccountSoapBindingImpl</servlet-name>
<servlet-class>
com.bigbank.services.account.AccountSoapBindingImpl</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> -->
...
|
しかし、「使用すべきでない」から実際の削除までの維持管理を最も容易に行うには、Webサービスを完全に削除するために必要なリファクタリングを行うことです。私達のシナリオでは、使用すべきでなくなったWSDLファイルとXSDファイル、生成されたJava Webサービス・スケルトン・クラス、Webサービスのデプロイメント記述子など( 図7 )は、BigBankの次のリリースを行う際に削除する必要があります。
図7. BigBankを廃止する際に削除すべきコンポーネント
もし皆さんのWebモジュールが他のWebサービスを含んでいる場合には、そうしたWebサービスに必要なファイルやWebサービス・デプロイメント記述子のエントリーなどを削除しないように注意する必要があります。
ビジネス・ロジックのインターフェースも、整理する必要があるかも知れません。私達のシナリオでは、Account EJBの古いcreateメソッドは、(もはや使われることはないため)削除することができます。
ここで説明した手法を使うことによって、Webサービス・クライアントは、そのアプリケーションに必要な変更を行うためにリリース・サイクルの全期間を活用できるようになります。またサービス・プロバイダーがこの手法を使用すれば、後方互換ではない変更を継続可能な形でサポートすることができ、古いインターフェースを無期限にサポートする必要がなくなります。
この記事で説明した、Webサービスの変更を管理するための手法と指針は、どのようなSOAガバナンス・プロセスにとっても貴重な助言となるはずです。こうした手法や指針は、企業が俊敏性を維持するためにサービスの変更を行う際に、その変更戦略を実装する上で開発チームが重要な役目を果たすことを示しています。また、変更がサービスのプロバイダーとコンシューマー双方に及ぼす影響を最小限にとどめる上でも、こうした助言が役立つはずです。予測性を持ち、またビジネスがサービスの変更を管理する必要があることを認識した戦略によって、関連コードの保全性や維持管理容易性を改善できる一方、変更とその変更が持つ潜在的な影響に関して、明確なコミュニケーションが可能となります。
貴重なコメントをくださったPeter ChilcottとPaul Dreyfus、そしてCharles Rayに感謝いたします。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Zipped workspace | workspace.zip | 182KB | HTTP |
学ぶために
-
Patterns: Service-Oriented Architecture and Web Services, SG24-6303-00、Endrei M.らの共著(IBM Redbooks、2004年4月)
-
Webサービス・バージョン管理のベスト・プラクティス、Brown K.とEllis M.の共著(developerWorks、2004年1月)
-
Webサービスに後方互換性のあるマイナーチェンジ、Butek R.著(developerWorks、2004年11月)
- Service Versioning - CBDI Journal Best Practice Report、Dodd J.とWilkes L.の共著、(CBDI、2006年2月)
製品や技術を入手するために
-
皆さんの次期開発プロジェクトを
IBM trial software
を使って構築してください。developerWorksから直接ダウンロードすることができます。

Mark Endreiはオーストラリアのメルボルンにある、IBM Application ServicesのIT Architectです。専門分野はSOAやWebサービス、J2EEアプリケーションのアーキテクチャーと実現などです。連絡先はMark.Endrei@au.ibm.comです。

Mario Gaonは、オーストラリアのメルボルンにある、AMS Architectural ServicesチームのSenior IT Architectです。J2EEやCOTSパッケージ、データ・ウェアハウス・プラットフォームなど、様々な技術領域にまたがるソリューション統合に長い経験を積んでいます。連絡先はmariogao@au.ibm.comです。

Justin Grahamは、オーストラリアのメルボルン在住のAssociate IT Architectです。1999年に卒業以来、IBMで働いています。専門分野はWebサービスやJ2EEアプリケーション、パフォーマンス・テストなどです。連絡先はjusting@au.ibm.comです。

Kerard Hoggは、Certified IT Architectとして、テレコミュニケーション業界で30年以上の経験があります。専門はSOAであり、テレコミュニケーション業界において、WebサービスやJ2EE、BPEL4WSなどを積極的に使用する幾つかの重要プロジェクトの指導的アーキテクトとして働いてきました。

Neil MulhollandはオーストラリアのメルボルンにあるAMSのSenior Technical Specialistです。専門はWebサービスやXML、J2EE技術、Websphere Application Server、オブジェクト指向設計などです。連絡先はneilmulh@au.ibm.comです。