レベル: 初級 Brian Maso, SOA and Services Guru
2008年 6月 10日 操作状態モデリングとは、詳細で一貫性のあるサービス仕様を作成するための手法です。サービスの振る舞いを操作状態モデルに照らし合わせることによって、サービス実装の有効性を客観的に確認する方法を学んでください。
私は常々、文章以外に Web サービスの振る舞いを記述する手法がないことにやる気をそがれ、苛立たしさを感じています。サービス設計者がサービス・インターフェースの各操作がどのような動作をするのか記述する能力は、時代を 250 年前に移したとしても何ら変わりはありません。現在の設計者はワード・プロセッサーを使って仕様書を書きます。同じ設計者が 250 年前に生きていたとしたら、インクと羽ペンを使って、やはり長々とした同じ仕様書を書くことでしょう。編集技術は別としても、サービス設計者は実質的に同じ文書を作成するはずです。
リスト 1 は単純なサービス (TellerService) に相当する Java™ インターフェースで、2 つのサービス操作を持っています。そして、このインターフェースはサービス・アーキテクトが一般的な銀行のために作成したという前提です。ここで、リスト 1 での 2 つのサービス操作 (debit と credit) の記述を読んでみてください (それぞれ Operation to debit an account by amount (金額 amount を口座に対して debit する操作)、Operation to credit an account by amount (金額 amount を口座に対して credit する操作))。このサービス記述を基に、サービス実装者は debit と credit 操作の実装に適合するサービスを開発できるでしょうか。
リスト 1. TellerService に相当する Java の 2 つの操作
public interface TellerService {
/**
* Operation to debit an account byamount
**/
public void debit( String accountNumber, double amount );
/**
* Operation to credit an account byamount
**/
public void credit( String accountNumber, double amount );
}
|
他にも情報がない限り、この 2 つの操作の実装に適合するサービスを開発することはできません。上記の記述は簡潔でわかりやすく、操作も単純ですが、debit という用語が「口座への振り込み」を意味するのか、「口座からの引き落とし」を意味するのかが曖昧です。あるいは debit が、このどちらでもない別のことを意味する可能性もあります。
さらに重要なのは、他の人との問題です。例えば、混乱したサービス実装者が自分の辞書で debit と credit を調べるとします。彼は、この 2 つの用語の意味がわかったと考え、その意味に従ってサービス操作を実装します。一方、同じ会社の別のエンジニアリング・チームでは、debit/credit サービス・クライアントを作成するというタスクが別のエンジニア (他の人) に与えられています。このクライアント・エンジニアはサービス実装者と同じ辞書を持っていません。彼女は仕様のなかに聞き慣れない用語を見つけ、いとこの会計士に E メールで助けを求めました。彼女はそのいとこから聞いた debit と credit の定義に従ってクライアント実装を開発します。
ここで大きな問題となるのは、サービス実装者とクライアント実装者との間で debit と credit の定義が一貫しているのか、あるいは矛盾しているのかということです。この 2 人がそれを知る手段はあるのでしょうか。定義が矛盾するとしたら、彼らはその矛盾に気付くでしょうか。定義の違いはそれぞれのエンジニアのプロジェクト・スケジュールにどう影響してくるのでしょうか。さらに、矛盾はどうやって解決されるのでしょうか。
通常、このような矛盾は開発サイクルの後のほうになってからでないと見つかりません (大抵は、統合テストの最中)。サービス・プロジェクトとクライアント・プロジェクトの両方のスケジュールには、プロジェクト・サイクルの終了間際になって悪影響が及ぼされ、場合によっては非常に大きな問題になります。これはまさに、プロジェクト・マネージャーがスケジュールの問題が浮上してきてほしくないと願うタイミングそのものです。
現在のサービス記述では、私たちは奇妙な状況に置かれており、サービスの振る舞いを記述するには文章にして作成する方法に頼らざるを得ません。しかし文章として作成したものは、仕様を読む人がそれぞれ独自の解釈をするという点で主観的なものです。2 つの異なる解釈が妥当で論理的であったとしても、互いに矛盾しないという保証はありません。さらに重要なことは、サービス実装が仕様に適合するかどうかを判断する客観的な基準がないという点です。このような状況は許しがたいと思います。
操作状態モデリング
操作状態モデリングはサービスの振る舞いを記述するための一般的手法で、文章で作成した仕様に常に付いて回るサービス仕様の矛盾、多義性、主観性、そして曖昧性の問題を対象としています。サービス仕様の矛盾、多義性、主観性、曖昧性はサービス実装プロジェクトに深刻な影響を及ぼしかねません。文章で作成した仕様がまず始めに来る実装プロジェクトでは、同じプロジェクトを操作状態モデルが含まれる仕様から始める場合に比べ、実装に何時間も (あるいは何ヶ月、さらには何年も) 多くの時間が必要となり、プロジェクト期間もいくらか長くなるはずです。
入力、出力、振る舞い
WSDL (Web Services Description Language) 文書は有効な入力メッセージと出力メッセージ、つまりどのようなメッセージが有効な操作入力となり、どのようなメッセージが有効な操作出力となるかを記述します。WSDL ファイルに含まれるメッセージ・スキーマは客観的であり、機械が読み取れるものですが、WSDL スキーマはサービスを十分に記述するために必要な内容の半分も満たしていません。一貫性があり、曖昧さがないようにサービスの振る舞いを記述するには、遥かに多くの情報が必要です。
例えば、ある操作の WSDL ベースの入力/出力メッセージ記述では、基本的にその操作はあらゆる有効な入力メッセージを受け入れ、あらゆる有効な出力メッセージを返すと規定しているとします。つまり、考えられるあらゆる有効な入力に対し、サービスがどのような出力メッセージで応答しても、その出力メッセージが有効であればサービス WSDL に準拠するということです。この場合、どんな入力に対しても常に障害メッセージで応答するサービス実装は使い物にならないはずですが、それでも WSDL には完全に準拠することになります。
そこには、入力メッセージを出力メッセージと結び付ける肝心な要素が欠けています。この欠けている要素を補うのが、操作の振る舞いについての記述です。WSDL の 2 つのデータ・ポイント (入力および出力セッメージ・スキーマ) は重要ですが、操作を完全に記述するにはそれ以上のものが必要です。
操作状態モデリングでは、操作には以下の 4 つのデータ・ポイントがあるという見方をします。
- 初期サービス状態
- 入力メッセージ
- 最終サービス状態
- 出力メッセージ
操作状態モデリングの視点では、サービス操作の呼び出しは以下のように行われます。
- 呼び出しを開始する前のサービスは、インターフェース・データ・モデルに準拠した初期状態にあります。
- 操作の WSDL 入力メッセージ・モデルに準拠した入力メッセージを受け取ったサービスは、その操作の呼び出しを開始します。
- サービス呼び出しの一環として、サービスは最終状態に置かれます。この状態もまた、初期状態を定義するインターフェース・データ・モデルに準拠したものです。さらに、この最終状態は初期状態と入力メッセージから予測することができます。
- サービスが、操作の WSDL 出力メッセージ・モデルに準拠した出力メッセージで応答します。この出力メッセージも、入力メッセージ、初期状態、および最終状態から予測することができます。
図 1 に、操作状態モデルがサービス操作に関する従来の WSDL モデルをどのように拡張するかを示します。操作状態モデルは WSDL モデルに含まれる入力および出力メッセージ・スキーマをインターフェース状態データ・モデルと 2 つの関数で拡張します。この 2 つの関数が、入力メッセージ、初期状態、最終状態、出力メッセージを結び付けます。
図 1. 操作状態モデルが操作の振る舞い情報を追加して WSDL モデルの入力および出力を拡張した様子
インターフェース操作での内部データ・セットの共有
質問です。WSDL はなぜ、サービス操作をインターフェースにグループ化するのでしょうか。その答えを知るには、n 個の操作を 1 つのセットとして持つ単一のインターフェースを定義した場合、あるいは n 個の操作のうちの 1 つを持つインターフェースを n 個定義した場合を考えてみてください。n 個の操作をインターフェースに区分化するこの 2 つの対照的な方法には、振る舞いの点で違いはありません。WSDL では、どちらの方法でインターフェース操作を区分したとしても、記述されるサービスはまったく同じになるはずです。図 2 に 1:n のインターフェースと操作の区分を、図 3 に n:1 の同様の区分を示します。この 2 つの図を見るとわかるように、いずれの区分化方法を使っても、基本的には同じサービス、つまり同じ n 個の操作に対応するサービス URL を記述することができます。
図 2. インターフェース対操作が 1:n の場合
図 3. インターフェース対操作が n:1 の場合
WSDL 仕様では、インターフェースが存在する理由も、インターフェースをどのように使用するべきかについても規定していません。しかしユーザーは直観的に、インターフェースがグループ化している操作は、関連するサービス・データに対してクエリーを実行したり、変更を行ったりするものであることを理解します。これは、インターフェースに定義された操作を支援する共通のデータ・セットがあると言っているのと同じことです。それぞれのインターフェース操作は共通データ・セットからデータを抽出したり、共通データ・セットに対してデータを更新したりします。2 つの操作がアクセスまたは変更するデータ項目に概念的な関連性がない場合、これらの操作が同じインターフェースに組み込まれることは通常ありません。同じインターフェースに組み込まれたとしたら、不十分な WSDL 設計になってしまいます。
TellerService とその debit および credit 操作の仕様に、今度は単純なインターフェース・データ・モデルを追加してみます。図 4 に示すエンティティー・リレーションシップ図 (ERD) は、TellerService の抽象データ・モデルです。このモデルには、debit および credit 操作の振る舞いを記述するに足る情報があります。TellerServiceState エンティティーには Account エンティティーのセットに対する関連 accounts があり、各 Account エンティティーには accountNumber ストリングと balance という実際の数値があります。この単純なモデルを使用すれば、曖昧さがなく、客観的で検証可能な debit および credit サービス操作の定義を作成することができます。
図 4. TellerService の操作に対応した TellerServiceState 抽象データ・モデル
リスト 2 は、TellerService の内部データ状態と、debit および credit の呼び出し結果による出力メッセージの値を定義するステートメントの論理セットです。このリストからわかるように、debit はある金額を口座残高に加算し、credit はある金額を口座残高から差し引きます。この記事の冒頭で指摘した多義性と主観性は、インターフェース・データ・モデルを TellerService 仕様に追加することで一掃されています。
リスト 2. TellerService の操作、debit および credit を記述する論理ステートメント
init-state is an instance of TellerServiceState
final-state is an instance of TellerServiceState
for each successful invocation of debit(accountNumber:string,
amount:real) on a service that starts with init-state
and ends with final-state it is true that:
the-account is an Account instance in init-state.accounts
where the-account.accountNumber = accountNumber
other-accounts is a set of Account instances where
other-accounts is a subset of init-state.accounts
and
(for each acc that is a member of other-accounts
it is true that acc.accountNumber != accountNumber)
the-updated-account is an Account instance where
the-updated-account.accountNumber =
(for each acc that is a member of init-state.accounts
acc.account-number != accountNumber implies
acc is a member of other-accounts)
the-updated-account.balance =
the-account.balance + amount
final-state.accounts = other-accounts + the-updated-account
for each successful invocation of credit(accountNumber:string,
amount:real) on a service that starts with init-state
and ends with final-state it is true that:
the-account is an Account instance in init-state.accounts
where the-account.accountNumber = accountNumber
other-accounts is a set of Account instances where
other-accounts is a subset of init-state.accounts
and
(for each acc that is a member of other-accounts
it is true that acc.accountNumber != accountNumber)
the-updated-account is an Account instance where
the-updated-account.accountNumber =
(for each acc that is a member of init-state.accounts
acc.account-number != accountNumber implies
acc is a member of other-accounts)
the-updated-account.balance =
the-account.balance - amount
final-state.accounts = other-accounts + the-update-account
|
ここで理解しておかなければならない重要な点は、図 4 に示した TellerServiceState の ERD はサービス実装のデータベース・モデルやその他同様のオブジェクト・クラスのセットを除外してはいないということです。リスト 2 の論理ステートメントでも、コードの特定の編成やスタイルを除外していません。TellerServiceState の ERD と論理ステートメントは、操作状態モデルのなかでの抽象化に過ぎず、TellerService 仕様に準拠したサービス実装を記述するためだけに存在します。実装の内部データ状態が TellerServiceState の ERD に従っているような動作をする TellerService 実装で、その振る舞いがリスト 2 の論理ステートメントとも一致しているものは、仕様に準拠した実装ということになります。データベース設計者と中間層のプログラマーは、それぞれの層を必要に応じて自由に実装することができます。
操作状態モデルのフィーチャー
操作状態モデルには、以下のフィーチャーがあります。
- サービス・インスタンスの内部データ状態を対象としたデータ・モデル。このデータ・モデルは、サービス・インターフェースに含まれるすべての操作が共有します。
- インターフェースの各操作を対象とした入力メッセージ・モデル
- インターフェースの各操作を対象とした出力メッセージ・モデル
- インターフェースの各操作の入力メッセージと初期サービス・データ状態から最終サービス・データ状態を予測するための関数
- インターフェースの各操作の入力メッセージ、初期サービス・データ状態、および最終サービス・データ状態から出力メッセージを予測するための関数
上記のうち、WSDL ではすでに 2 と 3 のインターフェース操作ごとの入力メッセージと出力メッセージをモデル化しています。この WSDL のモデルに加え、操作状態モデルにはインターフェース・データ・モデル (上記 1)、最終データ状態の予測 (上記 4)、出力メッセージの予測 (上記 5) が必要です。
内部データ状態のモデル化
操作状態モデルを作成するには、インターフェースの共有データ・セットの論理モデル、つまりインターフェース・データ・モデルを定義しなければなりません。各操作の振る舞いは、インターフェース・データ・モデルの観点から記述されます。
インターフェース・データ・モデルには UML (Unified Modeling Language) クラス図、エンティティー・リレーションシップ・モデル (上記の例のようなモデル)、またはリレーショナル・データ・モデルを使用することができます。いずれのモデル化手段も論理データ・セットを有効に定義します。結局のところ、どのモデリング・システムを使うかは主に以下の 2 つの問題によって決まります。
-
チームの経験と慣れ: 設計者 (または設計チーム) がよく使っているのがリレーショナル・データベース (RDB) であれば、当然 RDB モデルで抽象インターフェース・データ・モデルを設計することになります。一方、オブジェクト指向の支持者がサービスを設計する場合には、UML クラス図のほうが遥かに使いやすく感じられるはずです。ERD はこの 2 つの間を取った妥協策になります。
-
操作の XSD (XML Schema Definition) との組み合わせ: 操作の入力と出力は前述のとおり、XSD 型システムでモデル化されますが、設計者は UML クラス図、ERD、または RDB モデルのどれが最終的にインターフェース・データ・モデルのモデル化手段として選択されるかに関わらず、インターフェース・データ・モデルに対する操作の振る舞いを記述します。また、チームには XSD を UML クラス図、ERD または RDB モデルに変換する上での経験と技能が必要です。チームが特定タイプのモデルを得意としているとしても、XSD 変換を容易にするために、操作状態モデリングで別の種類のモデル化手段を採用しなければならないこともあります。私の経験では、XSD を ERD に変換するほうが UML クラス図に変換するよりも容易で、UML クラス図と RDB モデルを比べると、UML クラス図のほうが簡単です。
プロジェクトとチームにとって最も有効なモデル化手段を知る最大の手掛かりは、経験です。ここでは技術的な問題を説明していますが、モデル化手段の選択は明らかに、独断、慣例、そして個人的な好み、さらに商業的圧力と政治的圧力が伴うものです。プロジェクトに最適な技術オプションが選択されなかったとしても、適切なモデル化手段を使用してモデリングの目的を達成することはできます。
予測関数のモデル化
2 つの予測関数がそれぞれ記述するのは、出力メッセージ、最終サービス・データ状態です。これらの予測関数が、入力メッセージ、出力メッセージ、そしてインターフェース・データ・モデルを 1 つにまとめる「接着剤」となります。サービス仕様を読む人にとって非常に助かるのは、詳細が記述された部分と一貫性のある内容です。3 つのデータ・モデル (入力、出力、および抽象インターフェース・データ・モデル) が詳細を提供し、予測関数が一貫性を提供します。
予測関数の定義に最適なのは、命令型ではなく論理システムを使った方法です。つまり、プログラミング言語で出力と最終サービス・データ状態を予測するアルゴリズムを記述する代わりに、仕様の作成者は論理関数または数学関数を使って実際の実装に関する制約を記述しなければなりません。出力メッセージと最終データ状態の制約に適合した実装であれば、実装がどの言語で作成されていようと、あるいはコードがどのように編成されていようと、仕様に準拠したサービス実装ということになります。言い換えれば、客観的かつ検証可能な形で、実装が操作状態モデルに準拠するということです。
操作状態モデル予測関数の論理記述には、例えば以下を使用することができます。
-
OCL: UML には実は、論理制約言語が含まれています。あまりよく知られてはいませんが、それが UML バージョン 1.0 から UML 標準の一部となっている OCL (Object Constraint Language: オブジェクト制約言語) です。OCL は、ソフトウェア・プログラマーが親しみやすいように設計された論理言語です。OCL は命令型プログラミングとロジックとの概念的なギャップを埋めるのには非常に効果的ですが、UML に大きく依存します。そのため、UML にインターフェース状態モデルがない場合には、このオプションは検討対象から外してください。OCL は 1 次ロジックをとても見事に表現します。しかしその有効性は 1 次ロジックに限られ、アーキテクトがサービス要件として一般的に使用する複雑な概念は記述されません。その結果、要件の記述がまったく理解できないものになったり、コード化できなかったりすることさえあります。
-
HOL: HOL (Higher Order Logic: 高階論理) 言語はコンピューター・サイエンスや数学の学究的な分野以外ではほとんど知られていませんが、これは ASCII をベースとした高階論理の表記で、操作状態予測関数を構成するために使用することができます。学究的な起源を持つ言語であるにも関わらず、HOL は読みやすく、その簡潔さから言ってむしろ実用的な選択となります。ただし、HOL は論理言語です。つまり、プログラミング言語とはかなり勝手が違うため、慣れるまでに時間がかかります。リスト 3 に HOL の例として、TellerService の
debit 操作を HOL で記述した場合を記載します。HOL は、SourceForgeで積極的に保守されているオープンソースのロジック証明ツールの名前でもあります。
-
Z 記法 (およびその派生形): 英語では「ゼッド・ノーテーション」と発音される Z notation (Z 記法) は、ステートフルなソフトウェア・システムの振る舞いを記述します。元の Z 表記には不明瞭な数学記号を使用するという問題がありますが、操作状態モデリングとの概念的な相性は申し分ありません。事実、WSDL 2.0 標準委員会では Z 記法で WSDL サービス・モデルを記述した WSDL 2.0 仕様のバージョンをリリースしています。この数年の間、Z 記法のいくつかの派生形も開発されました。その多くは ASCII をベースとし、他の点でもオブジェクト指向プログラミングや命令型プログラミングを行うプログラマーにとって親しみやすいものになるように努力しています。HOL と同様、作成者は Z を使用して高階論理のステートメントを作成します。
-
アドホック論理ステートメント: リスト 2 は、TellerService 操作の振る舞いに関する制約を記述した一連の論理ステートメントの一例です。これらのステートメントは英語のように見えますが、実際は文論理ステートメントです。
リスト 3. HOL 風の操作記述 (リスト 2 と比較してください)
!debit. debit member-of valid-TellerService-debit-function implies (
debit member-of tuple(accountNumber:string, amount:real,
init-state: TellerServiceState) --> tuple(status:boolean,
final-state:TellerServiceState)
/\ (?the-account: Account. ?other-accounts: P Account.
the-account.accountNumber = accountNumber
/\ other-accounts = init-state.accounts - the-account
/\ (?the-updated-accout: Account.
the-updated-account.accountNumber = the-account.accountNumber
/\ the-updated-account.balance = the-account.balance + amount
/\ final-state.accounts = other-accounts + the-updated-account
)
)
)
!credit. credit member-of valid-TellerService-credit-function implies (
credit member-of tuple(accountNumber:string, amount:real,
init-state: TellerServiceState) --> tuple(status:boolean,
final-state:TellerServiceState)
/\ (?the-account: Account. ?other-accounts: P Account.
the-account.accountNumber = accountNumber
/\ other-accounts = init-state.accounts - the-account
/\ (?the-updated-accout: Account.
the-updated-account.accountNumber = the-account.accountNumber
/\ the-updated-account.balance = the-account.balance + amount
/\ final-state.accounts = other-accounts - the-updated-account
)
)
)
|
まとめ
操作状態モデルは、サービス・インターフェースを一貫性のある形で詳細に記述します。サービス実装が操作状態モデルに準拠するかどうかは、客観的に検証することが可能です。操作状態モデルには、以下の 5 つの特性があります。
- インターフェース・データ・モデル。多くの場合、UML クラス図、ERD、または RDB モデルで表現されます。
- インターフェースの各操作が持つ特性:
- 入力メッセージ・スキーマ。Web サービスの WSDL で表現されます。
- 出力メッセージ・スキーマ。同じく Web サービスの WSDL で表現されます。
- 最終データ状態の予測関数。OCL、HOL、Z 記法などの論理言語で表現され、入力メッセージおよび初期の抽象データ状態の観点から最終的な抽象データ状態に関する制約を記述します。
- 出力メッセージの予測関数。同じく論理言語で表現され、入力メッセージ、初期の抽象データ状態および最終的な抽象データ状態の観点から出力メッセージに関する制約を記述します。
操作状態モデルを使用したサービス仕様は、これを使用しない場合よりも優れた仕様になります。文章で記述した仕様に付きものの問題 (矛盾、多義性、主観性、曖昧さ) は最終的にはサービスの実装に必要な時間を (ものすごく) たくさん増やし、プロジェクトのスケジュールを大幅に遅らせることになります。同じ仕様でも、操作状態モデルを組み込めば開発作業を減らすことができ、プロジェクトのスケジュールが短縮されます。
参考文献 学ぶために
製品や技術を入手するために
-
IBM 製品の評価版をダウンロードして、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を使ってみてください。
議論するために
著者について  | 
|  | Brian Maso は、長年の経験を積んだ Java アーキテクトであり、実際のエンジニアです。彼は特に、Web サービス、ESB、公開サービス・ネットワークによるシステム・インテグレーション、そしてアジャイルなシステム仕様と単体仕様およびテストに関心を持っています。 |
記事の評価
|