このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

Webサービスの設計:Webの状態遷移図の描き方

前置きも書いたことだし、実質的な話を進めましょう。

以前の2つの記事をザッと読んでおいて欲しいのですが、ホントに大事なところはこのエントリー内でも繰り返します

内容:

  1. 画面遷移とはクライアント側の状態遷移のこと
  2. アクションが起動して結果を返すまで
  3. ハイパーオブジェクトとしての状態
  4. アクションノードの内部構造
  5. リクエスト辺と内部辺の切断
  6. ログイン処理の例
  7. 状態遷移をモジュールにする
  8. 状態遷移モジュールの意義

画面遷移とはクライアント側の状態遷移のこと

ここで状態遷移と呼ぶのは、Webクライアント(典型的な例はブラウザ)の状態遷移のことです。サーバー側の状態(リソース状態)は、今は考えないので注意してください。クライアント側の状態をアプリケーション状態と呼ぶこともあります(あんまり適切な用語法とは思えないが)。

ブラウザで考えるなら、クライアント側状態とは「画面」と思ってかまいません。ブラウザにログイン画面が表示されているなら、ブラウザはその画面で表現される状態にあります。画面遷移はすなわち状態遷移です。写実的に画面を描く必要はないので、画面=状態ノードはマルで表すことにします。

「画面=状態」という仮定は、クッキー(クライアント側ストレージ)とかAjaxとかを考えると修正が必要ですが、大幅な修正ではありません。とりあえずはクッキーやAjaxは無視して考えても差し支えないでしょう*1

クライアント側状態遷移はサーバーにより制御されます。次の図はサーバー側の処理を黒丸で表したものです。

HTTP GETリクエストを受けて静的コンテンツを送り返すだけのときも、WebサーバーによるGET処理は走ります。よって、どんな状態遷移でもサーバー側処理の黒丸を経由します。サーバー側処理を意識しないなら、上の図の点線で描いたような状態遷移となります。

基本はこれだけです。

アクションが起動して結果を返すまで

黒丸で表すサーバー側処理をアクションと呼ぶことにします。アクションが実行される前後の状況をもう少し精密に図示することにします。

番号を付けた各部は次の名称で呼んでいます。

  1. トリガー
  2. リクエスト辺
  3. アクションノード
  4. レスポンス辺
  5. 状態ノード

トリガーは、HTMLのアンカー(a要素)やフォーム(form要素)を抽象化した概念で、HTTPリクエストを発行するための文字通り“引き金”、あるいはリモコン・ボタンです。HTTPリクエストを表すリクエスト辺の根元となります。

白丸で表す状態ノードはブラウザ側状態の表現ですが、これは、「アクションの出力」「レスポンスデータ」「画面」などと同義です。トリガーにより、状態遷移が開始され、遷移後の状態が5番の白丸です。

ハイパーオブジェクトとしての状態

少しだけ複雑(?)な例を見てみましょう。

1番の白丸はハイパーオブジェクトです。ハイパーオブジェクトも、「アクションの出力」「レスポンスデータ」「画面」「状態」と同じことです。が、その内部にトリガー(ハイパーリンク)を含むかもしれないデータ(転送オブジェクト)という意味を強調したいときにハイパーオブジェクトと言います。

2番のトリガーは、1番のハイパーオブジェクト(状態ノードでもある)に含まれています。それは3番のリクエスト辺を通じて4番のアクションに繋がります。アクションから2本のレスポンス辺(5番と7番)が出てますが、これは論理OR/場合分けによる選択です。つまり、レスポンスが5番→6番の経路を通る場合と、7番→8番の経路を通る場合がありえます。

6番のハイパーオブジェクト(状態ノード)はトリガーを持ちません。「トリガーを持たないハイパーオブジェクト」も認めます。アンカーやフォームを持たないHTML文書がいくらでもあるのと同じことです。状態遷移としては6番に到着すると行き止まりになります*2。一方の8番ノードは内部にトリガーを持つので、さらに遷移を続けることができます。

アクションノードの内部構造

黒丸で表されるアクションノードは、ブラックボックスでありその内部を詮索すべきではありません。しかし、実用上はその内部構造を描きたくなることがあります。次の図の右側は、左側のアクションノード(黒丸)のなかを開いたものです。

外部から見れば1つのアクションノード(左の図)が、実は2つのアクションから構成されていたわけです。アクションどうしを結ぶ辺は波線で描くことにして、この波線は内部辺と呼びましょう。内部辺は、サーバー内で完結するリダイレクト、あるいはフォーワードのような概念を表します*3。内部辺(波線)がWebに出ていくことはなく、アクションノードどうしを繋ぐときにだけ内部辺を使います。

リクエスト辺と内部辺の切断

もうひとつだけ、絵の描き方のお約束を覚えてください(退屈でしょうが)。

リクエスト辺と内部辺はちょん切ってもいいとします。切ったところは矢印とY字の印で表します。切断したワイヤー(ケーブル)の端点は、プラグとコンセントのようになります。必要なときは繋いで元のようなワイヤー(リクエスト辺、内部辺)を回復できます。

切断したリクエスト辺と内部辺は、サブシステム(あるいはモジュール)間のインターフェースとして機能します。その実例はすぐ後で出します。

ログイン処理の例

実例としてログイン処理を挙げます。下図の3番の状態ノードはログイン画面です。8番の状態ノードは、3番でのログインが失敗したときに再度ログインを促す画面です。

全体の流れを時間順に眺めてみましょう。

  • 1番はリクエスト辺を切断したワイヤーで、一連の処理への入り口を提供します。
  • 2番のアクションは、最初のログイン画面を要求するGETメソッドを処理します。
  • その結果が3番のログイン画面。
  • 3番ノード(状態=ハイパーオブジェクト)は4番のトリガーを含みます。4番はサブミットボタンです。
  • サブミットボタンからは、ログイン処理に向かう5番のリクエスト辺が出ています。
  • 6番がログイン処理の本体であるアクションノードです。
  • ログイン認証が成功すると、7番の内部辺をたどることになります。実際の処理は未定です。
  • ログイン認証が失敗すると、あれ、番号忘れた、6番から出るレスポンス辺をたどります。
  • 8番は、ログイン認証失敗後のログイン再試行画面です。
  • 9番は8番ノードに含まれるトリガーですね。これもサブミットボタンです。
  • 10番のリクエスト辺は、最初のログイン画面と同じ6番アクションに繋がります。

状態遷移をモジュールにする

一連のログイン処理、それに伴なう状態遷移をパッケージング(カプセル化)しましょう。絵としては、機能的/意味的なカタマリとみなせるノードと辺の集まりを枠線で囲むだけです。下図の四角が枠線です。図中の「ログイン」は四角全体に付けたタイトルラベルです。

前節のログイン処理にユーザー登録を付け加えてみました。ログイン画面とログイン再試行画面に、アカウントをまだ持ってない人のためにユーザー登録画面に誘導するリンクを付けたとします。ユーザー登録処理はこのモジュールの範囲外なので、四角(枠線)から出て行くリクエストとなります。また、ログイン成功の後で何をするかも未定なので、四角から出て行く内部辺となります(これは一種の拡張ポイントです)。

モジュールに入り込む辺と出て行く辺は、リクエスト辺または内部辺を切断した“半分の辺”*4で表現されます。これは、「DI(依存性注入)からどこへ行こうか その1」とそこからリンクされているエントリー群で説明したポートベース・コンポネントになります。矢印のワイヤー端点がオス端子、Y字形のワイヤー端点がメス端子です。解釈や実装はだいぶ違いますが、でも、ポートベース・コンポネントの手法や意味論をかなり流用できます。

状態遷移モジュールの意義

先の図は、四角で囲んだ中身がスケスケに見えているのでホワイトボックス(あるいはグラスボックス)です。これを墨で黒く塗りつぶせばブラックボックスになりますね :-)。ホワイトボックスもブラックボックスも、Webシステムのビルディングブロックとして使えます。他のモジュールとのインターフェースは、図の四角に入り込むリクエスト辺/内部辺(正確には“半分の辺”)と、四角から出て行くリクエスト辺/内部辺です。状態遷移や処理の詳細は、四角の中にカプセル化されます。

この方法で一般的なモジュールやコンポネントのメリット(例えば再利用性)が得られますが、それより重要なことは、図形としての表現・記述が得られたことで、グラフ理論オートマトン理論/形式言語理論/圏論などを利用した解析の可能性が出てくることです。ごく簡単な例としては、有向グラフとして見ての可達性、ノード間の距離(ネットワークの最小ホップ数と同じです)、サイクルのあるなし/独立サイクルの個数などを計算できるようになります*5

Webシステムを表現する状態遷移グラフの道(パス)は、クライアントがそのシステム内をハイパーリンクをたどって動きまわったヒストリー(運動の軌跡)とみなせます。道(パス)の全体は自由圏とか余代数の構造を持ちます(「圏論番外:有向グラフのパスの圏(実装付き)」「グラフから作る道の余代数」)から、自由圏や余代数の計算が設計や分析になんらかの示唆を与えることも期待できます。

もちろん、図形的な表現から計算できる量が、現実的な意味を持つのかどうかは検討しなくちゃなりませんがね。

ともあれ、「図式化すればいいことありそうだ」と僕は思ってますが、もっと経験を積まないとホントのところは何とも言えません。とりあえずは、状態遷移図をイッパイ描いてみることからスタートですね*6

*1:まずは単純な状況で考えないと、話がややこしくなって混乱します。

*2:ブラウザの「戻る」ボタンとかによる遷移は話が別です。ここでの遷移はハイパーリンクをたどることによる遷移です。

*3:Catyでは、内部辺は単なるパイプ結合です。

*4:ファインマン・グラフの用語法では、half edgeとかflag(旗)と呼びます。

*5:図としてはペトリネットに似てます。が、ペトリネットと関係あるのかどうかはよく分かりません。ペトリネットの解析法は整備されているので、関係付けられたらいいのに、とは思います。

*6:ある程度は既に描いていて、「実務上使えそうだ」という感触は得ています。