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

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

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

参照用 記事

Webサービスを設計するための単純明快な方法

「Webサイト」、「Webアプリケーション」、「Webサービス」、「Web API」などの用語の区別はそれほど明確でもないし、きっちり区別して使うのもめんどくさいので、ここでは、これらを総称してWebサービスと呼んでしまうことにします。

山本陽平さんは、その著書『Webを支える技術』のなかで、人間がブラウザを使って利用するWebサイトとプログラム向けのWeb APIを区別すべきではないと述べています。この点は僕もまったく同感・同意です。

人間が相手となると、視覚的な効果や装飾、JavaScriptを使った操作性などにフォーカスが向けられ、Web APIとはまったく別物のような印象を与えます。しかし、各ページが持つべき情報やページ遷移の有向グラフ構造などは、相手が人間でもプログラムでも同じだと思うのです。そんな事情で、Webページの機能的/情報的なエッセンスを表現したHTML文書をクリーンHTMLと名付けてみました(以下の記事を参照)。

クリーンHTMLは、機能的/情報的な意味では完全な転送オブジェクト(transfer object; 後述)です。Webクライアントプログラムが必要とするデータを全て含んでいます。ただし、人間による解釈ではなくて 機械的にデータ抽出ができないとダメですよね。そのためにマイクロフォーマット/マイクロデータ(microformats, microdata)の技術が役に立ちます。特に今後はHTML5のマイクロデータが重要になると思います。

クリーンHTMLは、プログラムにとってはXMLJSONと等価であり、完全なデータです。人間(ブラウザ使用者)にとっても、機能的/情報的に不足はないはずです。とはいえ人間は、純粋な機能と情報では満足しないので、多くのWeb制作者の方々が大変な労力をかけているわけです。その労力による成果を、HTML文書のクリーンな構造に影響しない形で外部CSS/外部JavaScriptとして実現できれば、Webサービスの再利用性や頑健性に寄与すると思います。

もちろん、僕の言っていることは理想論で、機能・情報の構造とデザイン・使い勝手を分離するのが困難な状況もあるでしょう。ですが、意図的に努力すれば、かなりの程度は分離できて、それなりの効果も得られる、とも言えます。

今述べたことを実践するには、ある程度の指針や方法が必要です。そこで、僕自身が使っているやり方を紹介します。あまり完成度は高くないし、現状ではサポートツールもないのですが、単純明快なのがメリットです。

内容:

クライアント側の状態遷移に着目

Webサービスの設計と言うと、サーバー側のリソース設計と状態遷移の記述が中心になることが多いかもしれません。サーバー側設計はいずれにしろ必要ですが、最初は「クライアントからの見え方」をイメージしながら設計をしたほうが楽だと思います。「クライアントからこう見えるためには、サーバーはこうあるべき」と、サーバー側の構造は後から導出するほうがたいていは自然かと。

URL設計(使用するHTTPメソッド設計も含める)は重要なんですが、「クライアントからの見え方」のイメージができてないと、「どんなURLがいいのか」という議論もできないですよね。

Webサイトの場合、「クライアントからの見え方」というと、多くの人が画面遷移図を描きます。下の図の左のような感じ。画面デザインは問題ではないので、四角のなかを丁寧に描き込んだりする必要はありません。素早く手書きしたいとき、四角を描くのはけっこう面倒なので、僕はマルを使います; 下の図の右のようです。

「画面」という言葉は、機能的/構造的な観点からはクライアント側の状態(アプリケーション状態)と同義です。そして、クライアント側の状態遷移を引き起こすのは、HTTPリクエストに対するレスポンスです。このことは「Webアプリケーションの入出力と状態遷移」で述べました。 「クライアント側の状態遷移はサーバーにより引き起こされる」ことを加味して絵を描き直すと次のようになります。

ここで、白いマルから出ている矢印はリクエストです。黒マルはサーバー側で実行されるなんらかの処理を表します。黒マルから出る矢印はレスポンスで、遷移後の状態である白マルに向かいます。点線矢印は、以前に描いてあったクライアント側だけで考えた状態遷移です。リクエストを「関数呼び出し」の転送、レスポンスを「戻り値」の転送、クライアント側状態遷移を「代入文」で考えてもかまいません -- このテの解釈は、「Webアプリケーションの入出力と状態遷移」「Webベースの分散アプリケーション」に書いてあります。

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

白いマルはクライアント側の状態を表すのでした。ブラウザの場合、状態とは現在ロードされているHTML文書のことです*1。ブラウザではないWebクライアントであっても、状態はサーバーから直前に送られてきたXMLデータやJSONデータで決定されるはずです。サーバーからクライアントへとレスポンスに乗せて運ばれるデータを転送オブジェクトと呼ぶことにすると、次の言葉達は同義語です。

  1. クライアント側の画面
  2. クライアント側の状態
  3. クライアントに現在ロードされているデータ
  4. クライアントが直前に受け取った転送オブジェクト

転送オブジェクトの典型的かつ最も重要なフォーマットは(X)HTMLです。HTMLのホントにホントに重要な特徴、Webの根源ともいえる機能性は、そのなかにハイパーリンクを含むことです。具体的に言えば、a要素、form要素、link要素です。URL参照を含むimg要素、script要素もハイパーリンクに準じるモノと考えていいでしょう。

HTMLに限らない一般論を展開するときは、ハイパーリンクを含む転送オブジェクトをハイパー転送オブジェクト、あるいは単にハイパーオブジェクトと呼ぶことにします。ちなみに、「JSONデータをハイパーオブジェクトとみなしたい」という要求から生まれたのがJSONハイパースキーマ仕様です(うまくいくとは思えないが)。

トリガー、アクション、バインディング

状態を表す白いマルからリクエストの矢印を引くとき、矢印の根元に横棒を引きました。

この横棒を僕はトリガーと呼んでいます。トリガーは、有り体に言ってハイパーオブジェクトのなかに含まれハイパーリンクのことです*2。同義語を増やすのは好ましくないのですが、単にリンクで「繋がっている」だけではなくて、「引き金を引く」、「キッカケを作る」という意味を持たせたかったのです。では、トリガーは何を引き起こすのでしょうか? それは、サーバー側の処理です。Webサーバー自身、あるいはWebサーバーから起動されたプログラムによる処理の引き金となります。

トリガーには次のような情報が含まれます。

  1. URL -- a要素/link要素のhref属性、form要素のaction属性に相当
  2. HTTPメソッド -- form要素のmethod属性に相当
  3. 処理の意味や振り分けを指定するヒント -- link要素のrel属性や標準化された動詞に相当
  4. 入力データ -- form内部のinput要素などに相当

このなかで、「処理の意味や振り分けを指定するヒント」と「入力データ」は、いくつかのエンコーディング方法があります。「URLパスの一部にする」「クエリ文字列とする」「POSTデータとする」などです。エンコーディング方法を完全に標準化するのは難しいでしょう。当面、ケースバイケースで決めるしかないと思います。以下では、「処理の意味や振り分けを指定するヒント」としては、「そろそろ決着、HTTPメソッド、URL、そして標準化された動詞」で述べた動詞(verb)を使います。「入力データ」は、GETならクエリ文字列、POSTならPOSTデータを使うとします。

トリガーにより実際に引き起こされるサーバー側の処理はアクションと呼びましょう。アクションの実装はなんだっていいのですが、概念的に言えば、「URL、HTTPメソッド、動詞(オプショナル)」で特定される処理単位です。入力データがあってもかまいません(なくてもかまいません)。処理結果として(成功すれば)ハイパーオブジェクトを返します。

念のため注意すると、ハイパーオブジェクトはハイパーリンク(トリガー)を含んでもいいデータです。まったくハイパーリンクを含まない単なるデータ転送オブジェクトも、ハイパーオブジェクトと呼んで問題ありません。

具体的なアクションを特定(識別)するには「URL、HTTPメソッド、動詞(オプショナル)」が必要ですが、アクションを抽象的な計算処理と考えれば、与えられた入力に対して出力となるハイパーオブジェクトを返す関数です。アクションの仕様とは、「入力 → 出力(サーバー側の副作用があるかも知れない)」という関数としての仕様のことだとします。この意味でのアクションの仕様は、サーバーのロケーションとか使用するHTTPメソッドなどからは独立です。

ある程度抽象化されたアクションを、現実のWebに接地させる情報が「URL、HTTPメソッド、動詞(オプショナル)」であり、これはアクションのHTTPバインディングということになります。関数の抽象的仕様と、具体的な呼び出し方やデータフォーマットを分離するという発想は、IDL(interface definition/description language)に由来するものです。今僕が言っていることは、REST後のRPC回帰と呼べるかもしれません(まー、レッテルはどうでもいいですけど)。

把握可能、解析可能なWebサービス構造

疲れてきたので、基本概念を紹介したところでもうオシマイにしようと思います。書き残したことで気になることをごく短く触れておきます。

「Caty:サイトのハイパーリンク構造を把握する」において、Webサイトのハイパーリンク構造を有向グラフとして完全に描きたいと述べました。そのためには、URLとハイパーリンクを静的に完全に列挙する必要があります。ですが、「URLの書き換えやマッピングは一切しない」と方針を維持するのは現実には困難だと感じています。ファイル実体を持たないURLや、ある程度のURLマッピングを認めざるを得ないようです。

しかしそれでも、把握可能/解析可能な構造にはこだわっています。リソース実体を収めるファイルシステムだけからWebサービス構造を把握できなくても、アクションの仕様とHTTPバインディングの情報から有向グラフが描けるなら目的は達せられます。Webサービスの構造を表現する有向グラフが作れるなら、あとはなんとでもなります。

有向グラフの白いノードは「クライアント状態=ハイパーオブジェクト=アクションの戻り値」です*3。黒いノードは「サーバー側処理=アクション=拡大解釈されたリソース」です。有向グラフの辺は、具体的ロケーションへの指示・参照だけではなく、メッセージング、データフロー、入出力の型などのさまざまな属性(辺ラベル)を持ちます。ハイパーオブジェクトの型定義とアクションの仕様(宣言)は、グラフノード近傍の局所的な形状を決めます。URL設計/ハイパーリンク設計は、たくさんのノードをその局所的な形状制約を尊重しながら繋ぎ合わせる作業になります。

現状、このような発想や方法を支援するツールが何も無いのがつらいところです。が、定式化がシッカリすれば、ソフトウェアによるサポートが特別困難だとは思っていません。

[追記]元気があるときに、また続きは書くでしょう。計算処理の入出力/副作用のレベルと、それをURLとHTTPで具体的に表現するレベルを区別したほうがいいよ、ってことです。ただし、闇雲に何でもPOSTに突っ込んだりすると、Webの仕組みを無視した「悪しきRPC」に舞い戻ってしまうので、節度あるバインディングをしましょうね、ってこと。もともとIDLのバインディングマッピング)は、実装言語の機能性や特徴を活かした対応を考えることなんだから、HTTPバインディングだってHTTPのポリシーとメカニズムを最大限尊重するのは当然なんです。[/追記]

*1:クッキーとかのローカル状態もあるでしょうが、それを考慮しても本質は変わりません。

*2:正確には、本来の意味のアンカーです。が、単純インラインリンクしか使われてない現状において、リンクとアンカーを厳密に区別してみてもあまり意味がないでしょう。

*3:クライアントの初期状態はアクションの戻り値とは言えません。