状態遷移(プロトコルモデル)の実装 【パターン】

採用支援業務の「選考状況」の管理を分析している。
RDRA(リレーションシップ駆動分析)のプロトコルモデル(ステートマシン図)と、イベントモデル(イベント図)を使いながら、だいぶ、分析レベルのモデルとして骨格がはっきりしてきた。

業務の主要な関心ごとが、「状態」の視点と、「アクションイベント(状態遷移のトリガー)」の視点から、立体的なモデルになってきた。

この「状態(state)」と「イベント」のモデルを、「イベントソーシング」スタイルで、実装する設計を始めてみた。

初期の設計モデルは、こんな感じ。

構造


選考管理の主要クラス

UIから、わたされた、採用担当者のアクションを、サービス層の「選考アクションサービス」クラスが、処理する。

サービス層のクラスは、ファサードにして、実際のビジネスロジックは、ドメイン層の、

・選考プロシジャ   (業務ルール)
・選考状況リポジトリ (記録)
・選考メッセージング (通知)

3つのクラスに委譲する。

業務のオペレーション、つまり「起きたこと」は、 「選考アクション」イベントオブジェクトで表現する。

ファウラーの Domain Event パターンをそのまま使っている。
イベントオブジェクトは、イベントタイプオブジェクト(選考アクションタイプ)と、サブジェクト(関心ごと)である「選考コンテキストの現在の状態」を参照する。

振る舞い


選考管理の振る舞い

選考アクションサービスは、採用管理者が指示した「候補者」をキーにして、「現在の選考状況」を取りだす。
「コンテキスト」パターンにしているのは、選考履歴とか、選考の関係者とか、選考書類とか、その他のさまざまな情報の参照ポイントにするため。

「選考状況」をユーザに戻す(画面に表示する)時に、「選考プロシージャ」オブジェクトに、「選択可能なアクション」を問い合わせる。

状況ごとに、可能なアクションは、異なる。「選択可能なアクションリスト」を、画面では、「アクションメニュー」として、表現する。

ユーザがあるアクションを選択すると、再び、「選考プロシージャ」オブジェクトに問い合わせて、そのアクションのタイプを実行するための「ひな型(プロトタイプ)」として「選考アクション」オブジェクトを作成する。

選考理由とか、選考結果とか、必要な情報を入力するためのオブジェクト。

画面のフォームにマッピングして表示。
ユーザの入力内容はオブジェクトにバインドした結果を、「選考アクションサービ」が受け取る。

「選考アクションサービス」は、「選考プロシージャ」オブジェクトに、アクションの妥当性検証と、ドメインオブジェクトの現在の状況の変更とか、履歴への追加とか、さまざまな反映処理を、「委譲」する。

検証&反映が、無事に終わったら、「記録」すべきこと、「通知」すべきことの「業務のルール」を、「選考プロシージャ」オブジェクトの問い合わせる。

問い合わせ結果に応じて、リポジトリクラス(永続化インタフェース)を使って、適切に記録。
メッセージングクラス(通知インタフェース)を使って、しかるべき相手に、「変更が起きたこと」あるいは「処理が終わったこと」を通知。

検討課題


「選考プロシージャ」オブジェクトが、「選考業務」の知識を表現している。
その知識の表現手段として、列挙型の「状況タイプ」「アクションタイプ」を用意する。

この設計で、変更が容易に安全にできるかの検討がもうちょっと必要。

・状況をもっと、細かく管理したくなった場合になにが起きるか?
・現在は許可している遷移を、不許可にする場合、なにが起きるか?
・許可していない遷移を追加した場合は?
・永続化した過去の選考履歴と、変更後の整合性は?
・ユーザの画面操作だけでなく、REST スタイルの API や、非同期メッセージング用のサービスとして、そのまま使えるか?
...

まあ、初期のモデルなので、実装を進めながら、ここらへんに対応できる、しっかりした設計・実装に育てていければと思う。

今のところ、足がかりモデルとしては、悪くないと思っている。

イベントソーシングスタイルを実装する

採用業務の状態管理の仕組みをモデリングしている。

状態管理を、イベントソーシングスタイルで、やるための、大まかなクラス構成を絵にしてみた。
それぞれのクラスの役割をできるだけ、単純明快にすることを、重視した。

関心ごとの分離( SoC : Separation of Concerns )原則、 単一責任原則 ( SRP : Single Responsibility Principa ) をこころがけたつもり。

イベントソーシングの実現モデル

メッセージング / Web アプリケーション


上半分は、メッセージングのフレームワーク Mule ESB での実装をモデル化したもの。
下半分は、(今までやってきた) Web アプリケーションの MVC モデル、サービス層/ドメイン層/インフラ層のレイヤ構造を整理したもの。

Mule ESB のサービス部品と、Web アプリで使う Service クラスとは、完全に一致していない。
どちらも、薄いファサードで、実際の責務は、下のドメイン層のクラスに委譲するのは、同じパターン。

たぶん、コアのサービスクラスを作って、それを、 Mule ESB 用にラッピングしたクラスと、Web アプリケーションのサービス層用にラッピングしたクラスを、それぞれ用意することになると思う。

ドメイン層のイベント処理モデル


Event と Document


Event クラスは、シグナルに近い。 単純に「変化があった」ことだけを表現する。
Document クラスは、その event に付随する「その時の状態」を表現したもの。

この二つは、発生時点の状態を、feeze して記録する。つまり、不変(immutable)オブジェクト。

State


State オブジェクトは、業務上の関心のあるオブジェクト。
「現在の状態」とか「履歴」とか「次にやるべきこと」、「予定日時」とか、業務上、参照したい情報を置いておく場所。
図には描いていないけど、State オブジェクト ( リファレンスオブジェクト、または Entity )は、参照用の識別キー(ID とか管理番号)が必須の属性。

こちらは、業務で変化が起きるたびに、状態を変更していく 可変 ( mutable ) オブジェクト。

Procedure


Procedure クラスは、ドメイン層内部のレイヤ構造で、上位レイヤ(Policy層)に位置するオブジェクト。
このクラスに、
・ある event が発生したら、何をすべきか
・State オブジェクトの状態と、その変更の妥当性 (有効な状態遷移)
などの「業務手順の知識」を記述する。

実装技術


この絵で、白い背景の要素は、フレームワーク側で用意している仕組みや部品で実現しようと思っている。

Mule ESB だと、 inbound/ountbound 、メッセージ routing 、例外処理、などは、いろいろ部品がそろっているので、ビジネスロジックコンポーネント( Service クラス )と、委譲先のドメインクラスを設計・実装して、組み立てるだけ。
(というか、ドメイン駆動設計でやっているので、ドメイン層は、サービス層を実装する時には、できあがっている)

Web アプリケーションも、 コントローラーは、 Spring 3 MVC と、 Spring Web Flow が用意されているので、それを活用する。

いままでのシステムでは、 Validation は、if 文とか使って、validation 用のメソッドを、ごりごり書いていた。
今回の案件では、 JSR-303 Bean Validation を全面的に採用することにチャレンジ中。
アノテーションで、 @NotEmpty とか @Length とか、宣言するだけで、検証は、フレームワークがやってくれる。
if 文が、激減する。

O-R Mapping フレームワークとか、Java の Collection フレームワークを活用するようになって、もともと、for 文は、ほとんど、コードに登場しなくなった。

今回、アノテーション方式の Validation フレームワークを導入したので、if 文も、ほとんど見かけなくなる予定。

たぶん、サービス層やドメイン層で、if 文や for 文を発見したら、「いやな臭い」として、ドメインモデルの設計改善ネタにしてまちがいなさそう。

if 文や、for 文が激減していくのは、まさに、IoC ( Inversion of Control ) ですね。
「プログラムの制御文(control)」は、どんどんフレームワーク側に移動して、アプリケーション開発は、どんどん、宣言スタイルに進化(?)していく。

モデリングして、それを、アノテーションや XML で宣言、以上終了、という感じ。
テスト記述も、どんどん、アノテーションと、XML になってきちゃっている。

開発という仕事は、制御構造(if分岐、forループ)にどっぷりつかった「プログラミング」から、ドメインを表現する「ライティング」に大きくシフトしてきた感じ。


event とメッセージング 【きっと、良いパターン】

前の記事で、 state ソーシングスタイルと、event ソーシングスタイルについて書いた。

単純にいえば、メモリ上でオブジェクトを操作する順番、そして、データベース操作の順番の問題。

state ソーシング


state ( 現在の状態 ) オブジェクトの更新が最初。
必要が、あれば、audit ログ(監査記録)とか、history ( 履歴 ) オブジェクトを作成する。

テーブルであれば、state テーブルを update すると、トリガー で、監査テーブルにレコードを insert したり、history テーブルに insert する、というやり方。

顧客の連絡先、例えば、住所を変更したら、住所変更ログを書いたり、住所変更履歴を作成する、というパターン。

event ソーシング


ビジネスの事象オブジェクトを作成する。 そのオブジェクトが、しかるべき、state オブジェクトを変化させる。

入庫があったら、入庫イベントオブジェクトを作って、そのイベントオブジェクトを処理すると、「在庫」オブジェクトの現在高を更新。

入庫レコードを作成すると、トリガーで、在庫テーブルの「在庫数」を 自動更新。

書類選考の合格イベントを記録したら、「選考状況」オブジェクトのステータスを「書類選考合格」に更新する。

というような、パターン。

システム間の連係


今までの例は、ローカルなアプリケーションで、メモリ上のオブジェクトやデータベーステーブルでの操作の順序の話。

システム間の連係になると、event (変化が起きたこと)をメッセージング(通知)するのが基本パターン。

仕事探しのサイトで応募があったら、採用管理システムに通知。
採用管理システム側で、選考結果を変えたら、仕事探しサイトに通知して、さらに、メールで、応募者に通知。
...

人とシステムの連係であれば、何か、関心事に変化があったら、電子メールで通知する。
システム間であれば、変化があったら、メッセージを非同期で送る。(例えば、XML over HTTP で)

関心事に変化があったら、それを、イベントオブジェクトとして表現し、非同期メッセージングでしかるべきところに送る、というのが業務アプリの基本パターン。

event ソーシングパターンは、メッセージング(通知)という仕組みを使って、エンタープライズアプリケーション間を連係を実現する有力な手段。

event ソーシングスタイルは、

* メモリ上のオブジェクトの操作
* ローカルデータベースを使った永続化
* リモートシステムとの連係

に一貫して使える、基本パターン、ということ。

業務の基本、

* 起票 (イベントの表現 & 永続化)
* 記帳 (現在の状態の表現 )
* 連絡 (通知)

を、素直に、メモリ/DB/通信 で実装するには、 event ソーシングスタイルが、よさそう。

コマンド、イベント、ドキュメント


メッセージは、基本的に3種類ある。

コマンド・メッセージ


メッセージの主たる内容が、「指示」や「依頼」であるパターン。
リモート手続き呼び出しを、非同期にしたもの。

メソッド名と、そのパラメータセットを、そのままメッセージにする。

システム間の結合度はかなり強い。
非同期だけど、相手からの回答があることを、前提としている。
(少なくとも、こちらが指示した内容を、相手が実行することを期待している)

イベント・メッセージ


変化があったことだけを伝える方式。

どんな変化があったか、詳しく送る場合もあれば、「変化したよ」ということだけを通知する方法もある。

「変化したよ」だけの場合は、イベントメッセージを受け取った側が、必要だと判断すれば、詳細情報を、取りに行く。

イベント・メッセージの設計・実装で興味深いのは、

・イベントメッセージの送り先
・詳細情報の手に入れ方

を、送る側が意識しない、という設計ができる点。

イベントメッセージを、関係がありそうなところに、ブロードキャストすると、関心がある受信者だけが、それを処理し、また、必要な受信者だけが、必要な詳細情報を、自分で取りに行く、というパターン。

例えば、会社で、「お中元でビールをもらいました」というメッセージを社内のブロードキャストすると、ビール飲みたいやつが、しかるべき場所(冷蔵庫?)に、勝手に取りに行く、というやり方。

これは、システム間の結合を、劇的に「疎」にできるパターン。

というか、人が、ビジネスや社会活動をする時の基本パターンがこれ。

event ソーシングで、event メッセージング、というアーキテクチャスタイルが、これからのエンタープライズアプリケーション設計の基本パターンなんだと思う。

ドキュメント・メッセージ


メッセージの中身が「ドキュメント」、つまり、データだけのメッセージングのパターン。

これも、実際の業務では多いパターンですね。「書類送付」。
あるいは、システムでも、CSV ファイルを送る連係方式は、このドキュメントメッセージングのバリエーションですね。

ドキュメントメッセージは、「コマンド」つまり「指示」とか「依頼」は明示しない。
メソッド名なしに、パラメータセットだけ送る、という、ある意味、とても乱暴なやり方。

実際の業務では、多いパターン。

応募、注文のように、しかるべき「送り先」にドキュメントを送れば、しかるべき処理をしてもらえる、という了解の上になりたっている。

「送り先」がポイント。
あるいは、受け取った側が、ドキュメントの内容を判断して、処理をやり分ける、という仕組みも現実には多い。

アプリケーション間の結合度としては、「コマンド」と「イベント」の中間。

アプリケーション間である程度のデータ形式の合意が必要。
受け取った側が、自分の都合だけで、データ項目の取捨選択や解釈することが可能なので、コマンドメッセージングよりは、結合度が低い。

メッセージングの設計


「コマンド」「イベント」「ドキュメント」の選択。

コマンド・メッセージングの設計


これは、メソッドの設計そのもの。

メソッド名(コマンド名)、パラメータ形式、戻り値、例外、をそれぞれ設計する。

これを、アプリケーション間で合意するわけだから、結合度はかなり密になる。
変更が大変になるパターン。

イベント・メッセージングの設計


何を通知するか ( event タイプの列挙 )
誰に伝えるか ( 宛先、場合によっては、ブローカーの設計や、ブロードキャストの設計 )
どこまで伝えるか ( 詳細情報をメッセージに含めるか)

いろいろバリエーションがあって、なかなか興味深い、設計テーマ。

もちろん、答えは、「ドメイン」の言葉に耳を傾けること。
テクニカルな検討ではなく、「業務」でどうやっているかが、その答えになる。

ドキュメント・メッセージングの設計


乱暴にいえば、紙、画面のフォーム、csv ファイルなど既存の情報の表現を、XML 形式で表現すれば、設計完了。
通信方法は、HTTP という便利な仕組みがるので、XML 形式のデータ表現さえ設計すれば、 XML over HTTP で実装完了、というわけだ。

もちろん、XMLの組み立てとか、解釈とかの処理は必要だけど、最近は、O-X マッピングのフレームワークとかも、なかなか良い感じになってきたので、実装の負担はかなり減ってきている。

XML ドキュメントの 設計だけが課題、という感じ。

イベント+ドキュメントが基本


実際のアプリケーション設計は、

  • イベントメッセージングのための event タイプの設計
  • 詳細情報を伝えるためのドキュメントメッセージングの XML 形式の設計

の2つが、二大設計課題ということ。

ここがちゃんと設計できちゃえば、アプリケーションの骨格は、かなり固まるはず。

(ここまでは、仮説)
この仮説を、今回の、仕事探し+採用管理のシステムで、実践しながら、設計・実装ノウハウを獲得したいと思っている。

state ソーシング、 event ソーシング 【スタイルの選択肢】

業務の情報の扱い方の基本スタイルとして、

* state ソーシング
* event ソーシング

がある。

state が先か、event が先か、という正反対のスタイル。

state ソーシング


現在の「状態」を中心にしたスタイル。

state オンリー


一番、単純なパターン。
いわゆる「上書き」モード。

メモリ上のオブジェクトを、 setter とか update メソッドで、最新の状態を保持する。
更新前の状態は、どこにも残っていない。

テーブルであれば、update 文で、最新の状態に上書きしていくパターン。

もっとも単純だし、これで要件が満たせるなら、これで必要十分。

情報源は、「上書き」された最新の state だけ。 これが、state ソーシング のもっとも、単純な state オンリー パターン。

state - audit log パターン


state を変更した時に、記録を残すパターン。
audit log ( 監査ログ ) 。

業務アプリでは、金銭がからんだり、責任の所在を明確にするために、この要求は多い。

実装方法としては、 state オブジェクトを更新した時に、いっしょに、audit log を書くパターン。
データベースだと、state テーブルを update するたびに、トリガーで、audit log を自動的に記録する。

情報源は、 state で、そこから、 audit log を派生させるパターン。

state - history パターン


状態・履歴パターン。

audit log と基本は、いっしょ。
異なるのは、audit log は、アプリケーションの通常の機能では、アクセスしない。あくまでも、監査目的の情報。

それに対して、 history ( 履歴 ) は、アプリケーションで、その情報を利用することを目的としている。

大きな違いは、「history (履歴)」の、識別キー を使った参照が必要になること。

例えば、バージョン番号とか、人が、業務上、意識する、なんらかの識別キーが表にでてくるのが、 state - history パターン。

実装の仕組みとしては、 state - audit log パターンといっしょ。
state を更新して、 history ( 履歴 ) を派生させる。

ここまでは、情報の起点が、更新可能な state オブジェクトまたは、state テーブル。

state ソーシングのバリエーション。

event ソーシング


状態の変化を、イベント(事象)として表現するパターン。
state ソーシングでは、すべての情報源は、最新の状態(state ) に更新するところから派生させる。

event ソーシングでは、event が、起点になって、現在の状態 ( state ) を派生させる。

テーブルで考えるとわかりやすい。

event レコードテーブルの、 insert トリガーで、 state テーブルを更新する、というパターン。
例えば、入庫イベントを記録したら、現在の在庫数を更新する。

紙ベースの業務では、event ソーシングのほうが、自然なパターン。
まず、入庫という経済イベントが起きたら、伝票を作成(起票)する。それから、元帳に 記帳 して、残高を変更する。

これを、すなおに、実装すれば、 event ソーシングスタイルになる。

紙ベースだと、 event ソーシングがあたりまえなのに、電子化(データベース化)すると、state ソーシング が、けっこう幅を利かせている。

これ、電子化だから、ではなく、IT技術者が、業務をよく理解していないから、そうなっているように思う。

紙だろうが、電子だろうが、 業務は、個別の事象を、まず、 起票 して記録し、合算や残高の変更は、伝票から派生させるのが、基本。

理論的には、イベントの記録だけあれば、現在高は、導出可能。
もちろん、それでは、残高参照に時間がかかってしょうがないので、「現在高」も、ついでに更新しておく。

情報源は、イベント記録で、現在の状態 (state)は、導出された値。

state オブジェクト/テーブルは、キャッシュや、インデックスみたいな、高速化の技法で、あって、本質的なオブジェクトではない、ということ。

event ソーシングで行ってみよう


始まったばかりの採用支援プロジェクトでは、この event ソーシングスタイルに、こだわってみようと思っている。

応募イベントを起点にして、採用可否の決定までの業務のサポートを、

「採用プロセスで発生するイベント」を情報源(ソース)として、「選考状況 ( state ) 」を導出しておく、というパターン。

選考状況の変更を、関係者に通知する、という業務ニーズがいろいろあるんだけど、これも、「イベント」を通知(メッセージング)するスタイルで、モデリングし、設計・実装しようと思っている。

業務のモデルとして、それが自然だと思っている。
また、Mule ESB というメッセージングのフレームワークが、なかなか、いい感じで使えそうとう、実装技術面からのサポートもある。

自分も今までは、event ソーシングぽいことを、なんとなくやってきたけど、初期のモデリング段階から、event ソーシングを明確に意識して取り組むのは今回が初めて。

さて、どんなことになりますか。

<参考>
マーチン・ファウラーの PoEAA の続編 wiki の

Event Sourcing パターン

イベント駆動のアーキテクチャ 【これから重要になるパターン】

ユーザが、知りたいことを知る方法は、大きく二つ。

A.自分から、情報を取りに行く ( active )
B.通知を受け取る ( passive )

エンタープライズアプリケーションの多くの機能は、どちらかの方法で、「ユーザが情報を知る」ためのサービス。

情報を取りに行く


アーキテクチャとしては、REST スタイルで、 URI を指定して、GET 要求をするパターンですね。

画面系のアプリケーション。

業務的には、残高照会とか、在庫照会とか、いわゆる「照会」系の機能。
検索機能とか、一覧機能も全部、これですね。

GET.png

この場面での設計課題は基本的に二つ。

(1)ほしい情報の特定方法(識別方法)
(2)ほしい情報の表現方法(表現形式)

銀行口座であれば、「口座番号」で特定する。
表現方法も、「残高」を「金額」として表現すればよい。

実際の業務でも、もちろん、もっと複雑な課題がいろいろあるけど、

・識別キーの設計 ( URI 設計)
・表現の設計 ( XML 、HTML、JSON, CSV, PDF などの出力フォーマット )

が2大テーマ。

表現の設計は、XML,JSON,CSV のように、論理構造だけ設計するケースと、HTMLやPDF のように、体裁(視覚表現)まで設計するケースがある。

通知を受け取る


ユーザが、関心のある「なにか」が起きたら、それをユーザに通知するパターン。

アプリケーションの実装手段としては「電子メールで通知」が多い。

・振り込みがあったら、連絡する。
・応募があったら、通知する。
・審査結果を通知する。
...

これも、さまざまなシーンがある。

SEND.png

REST スタイルの情報提供に比べると、設計課題は少しややこしくなる。

ここらへんは、マーチンファウラーの Domain Event パターンの議論が参考になる。

ドメインイベント.png

関心事オブジェクト(Subject) の特定は、REST スタイルの GET 方式と同じで、識別キーの設計(URI設計)が、基本課題の一つ。

Event Type


大きな設計課題は、EventType の設計。
関心事オブジェクトの、どんな「状態の変化」に関心があるか、というモデリングと設計ですね。

関心事オブジェクト自体は、さまざまな属性が、いろいろタイミングで変化する。

例えば、「顧客」というオブジェクトから辿る情報として、取引履歴は頻繁に変わるし、連絡先とか、請求先も、変わる可能性がある。
購入の累積ポイントで、特別なサービスの権利を持つかもしれないし、与信限度額を超えて、一時的に受注不可になっているかもしれない。

すべての変化の可能性を列挙して、10も20もの EventType を設計するのが、完全なアンチパターン。

「連絡先」に変化と、「取引履歴」の変化は、別の関心事。
「与信限度額」とか「累積ポイント」とかは、取引履歴に関連はするけど、それぞれ、別の関心事。

こういう関心事を分類整理して、役にたつ「Event Type」のセットを設計するのが、重要な設計課題ですね。

イベント(状態の変化)に注目したアプリケーション機能の設計は、Event Type の設計がキモだし、ここがすっきりすれば、わかりやすく役に立つソフトウェアになることまちがいない。

Event


「状態の変化」を表現する Event オブジェクトの基本は、

・Subject への参照 (関心事の識別キーの保持)
・Event Type への参照 ( 何が起きたかの表現)
・発生日時
・記録日時 (イベントオブジェクトの生成日時)

の4つになる。

発生日時と記録日時が同じ場合も多いけど、これ、基本パターンは、Dual 日時にしておくこのが、実践的。

ビジネスの世界では、「起きた日時」と「それを検知した日時」が、それぞれ、重要な関心事になるケースが多い。

イベントの通知(メッセージング)


イベントと、その通知(メッセージング)は、混同されることが多い。

私は、

イベント:変化があったことの表現
メッセージング:通知する行為

を明確に別のものとして考えている。

メッセージングは、「イベント」だけに限らず、「シグナル」「ドキュメント」「コマンド」など、いろいろな通知対象がある。

もちろん、イベントはただ、オブジェクトを生成しただけでは役に立たない。

ビジネスでは、関心事に変化があったら、

(1)記録 (永続化、DBへの書き込み)
(2)通知 (メッセージング)

の2つを行うのが基本中の基本。

イベント駆動のアーキテクチャ


ビジネスの道具である、エンタープライズアプリケーションは、もちろん、この「永続化」と「メッセージング」が2大テーマ。

永続化は、データベース、O-R マッピングのフレームワークなど、アプリケーション実装技術の基本だし、なじみがある。

設計という視点でも、PoEAA, DDD など、どれも、エンタープライズアプリケーションの基本課題として「永続化」を中心に議論している。

それに比べると、イベントのメッセージングは、実装技術としては、電子メール通知くらいしか、やったことがない、という技術者が多いのかな?

設計パターンの議論も「永続化」に比べたら、情報は、極端に少ない。

「永続化」の視点の設計・実装は、データモデリングとか、データベース設計やSQLという歴史がある、分野。
エンタープライズアプリケーションといえば、画面系の仕組み(MVC)+「DBアクセス」というのが基本セットですね。

最近だと、画面系は、Web が多いから、 Web + DB = アプリケーションというのが、基本パターン。

イベント駆動で考える


IT側からみると、永続化とメッセージングは、現実的に、かなり、大きさが違う存在。

設計・実装を経験する機会は、「永続化」、データベースが圧倒的に多い。

でも、ビジネス側、エンタープライズアプリケーションの利用側で考えると、このIT側の「永続化」と「メッセージング」の重さの違いは、たぶん、異常な状態。

ビジネスの活動を素直に分析、モデリングすれば、

(1)記録 (永続化)
(2)通知 (メッセージング)

は、2大重要機能なんです。

個人的には、今、ここは、ホットスポットだと思っている。

イベント駆動のアーキテクチャ、「イベント」と、その「メッセージング」を重視した、モデリングと、設計・実装が、これからのエンタープライズアプリケーションでは、必須だし、また、実現できるシステム価値も、非常に大きなものになる。

技術的にも、インターネットを支える基盤技術があって、その上で、クラウドコンピューティングという、「ネットワークベース」の技術の重みが急速に拡大している。

また、Mule ESB のように、メッセージングを扱うためのフレームワークも、だいぶ、発展してきた。

XML/JSON over HTTP のような REST スタイルの「メッセージング」も、普通に使う技術になった。

画面アプリも、伝統的な Web MVC (サーバーサイドのコントロール)だけでなく、REST スタイルのサービスを使った、アプリ サイド MVC ( クライアント側のアプリのコントロール)が、広がってきている。
特に、iPhone や、 Android では、ネットワーク上の資源を活用したアプリが一気に花開きはじめた感じ。

そういう技術面の流れと、「記録」と並んで「通知」が重要、というビジネスの本質の要求が一体となって、これからしばらくは、イベント駆動スタイルのアーキテクチャ、そのためのモデリング、設計・実装技術が、ホットスポットだと思っている。

実践して、ノウハウ習得が必要


自分自身、イベント駆動スタイルのアーキテクチャに経験が多いわけでもないし、勉強しようにも、参考情報がなかなか見つからない、というのが実感。

EIP:Enterprise Integrations Patter とか、前述の、ファウラーの Domain Event の議論とかは、貴重な参考情報だと思っている。

これと、ドメイン駆動設計の考え方、設計手法としては、神崎さんの RDRA ( リレーションシップ駆動分析)と、ICONIX 、実装技術として、Mule ESB メッセージングフレームワーク、基盤技術として、 Java/Spring あたりで、いろいろ実践しながら、ノウハウを習得していきたいと思っている。

番号より名前。 ニーモニックコードより名前。 【パターン】

「識別キー」とか「ID」 というと、番号体系やコード体系のイメージが強いと思う。

39857, 10.13.xx.255 とかの数字ID
AK-4483 とかアルファベットと数字
UK, JP とかのニーモニックコード

などが、コンピュータの世界で、よく使われる形式ですね。

もちろん、一般の世界でも、

090-9999-0000 形式の電話番号、 113-0031 形式の郵便番号とかは、使っている。

私は、記述的な「名前」による識別のほうが、わかりやすく、良い識別方法だと思っている。

10.13.xx.255 より、somewhere.over.the.rainbow のほうが、わかりやすい。

ニーモニックも、プログラミングの世界ではあたりまえだけど、

cp より copy
mv より move

のほうが「はるか」にわかりやすい。自然言語を使った名前が、やっぱり一番。

ソフトウェア技術者は、番号やニーモニックを扱うことに慣れているけど、かなりの部分は、古びた悪しき習慣なんじゃないかと思っている。

ニーモニック


大昔、機械語しかなかった時代に、 LD(ロード命令)や、ST(ストア命令)というニーモニックは画期的だった。 0110 だとロード、1110 だとストアなんていうのが、LD / ST で書けるのは、ほんと革命。

この時代は、まさに、LD とか、 ST が「ニーモニック(覚えやすい)」コードだった。

Unix の初期の開発者たちは、こういう時代の人たちだから、ニーモニック大好き。

だから、Unix の初期の時代からあるコマンドは、ニーモニックのオンパレード。

cd, ls, cp, mv, dd, ls, sh, cc, ...

Unix( Linux ) で、文字数が多いコマンドは、まちがいなく、後から追加されたコマンド。 Unix 原始時代には、3文字、4文字のコマンドはなかった(と思う)。

50 bps レベルのテレタイプが主流で、300 bps が高速!通信回線だった時代。

メモリも、32KB とかで大容量。

この時代だと、copy より、cp の方が、効率が良い、というのが、大真面目な議論だった。
自然言語の「冗長」な表記は、とんでもない、ことだった。

もっとも、同時代の VAX は、help, link, run, ... というように、自然言語重視派。

Unix 初期の連中が、変わり者だったのかな?

まあ、いずれにしても、いまでも、ソフトウェア技術者の多くは、ニーモニックが当たり前だとおもっているし、自然言語の表記より、ニーモニックのほうが、好みかもしれない。

私は、自然言語派。プログラミングの世界といえども、人間にとっては、ぱっとわかりやすいのは、自然言語にきまっている。

だから、 def, val, var, << :: | とか連発するプログラミング言語は嫌いなんですよねえ。
生産性(可読性)が低いと思っている。

自然言語の文字列の処理という技術革命


Unix の初期開発者たちは、自分たちは、ニーモニック大好きだったけど、

The quick brown fox jumps over the lazy dog.

を単語に分解して、単語数を数えるプログラム、とか、自然言語の文字列を処理する、という分野で、コンピュータの世界に大きな変革をもたらした。

汎用コンピュータ用のOSは、「固定長」が絶対条件。
可変長とか、動的にヒープを獲得・開放するなんていう発想はなかった。

だから、プログラム設計、ファイル設計、通信電文設計、どれも、最大桁数をきめ、最初から固定で割り当てる設計する。一文字もデータがなくても、最大桁数をメモリやディスクに確保してしまう。
かなり資源の無駄遣いです。

どんな長さがくるか、予測できない、自然言語文字列の処理は、大の苦手だった。

Unix では、可変長、動的なリソースの獲得・開放を扱うための、データ構造とアルゴリズムにこだわった。
C 言語の勉強とは、データ構造とアルゴリズムの勉強だった。

また、ストリーム型の処理(文字単位で処理していく)も特徴。
メモリ上のバッファは固定長でも、データ自体は、可変長で、NL とか EOF が来るまで読み込む、という「可変長文字列の行単位の処理」というのは、画期的だった。

もっとも、一秒間に、5文字送るのがせいぜいのテレタイプ回線で、通信していたりすると、一文字ずつ、順番に処理する、という発想に自然になるんだろうけど。

Unix や、VAX では、可変長で、動的確保と開放が基本思想。現在のコンピューティングでは、もちろん、こちらが当たり前。

もっとも、OSの内部では、テーブル等は、固定長・固定数のことが多かった。
ぎりぎりの性能を重視すれば、可変長・動的割当は、かなり鈍重な処理ですからね。

長さ固定とか、コード化して短縮表記とかは、コンピュータが高価で、ソフトウェアも、原始的な時代の遺物に近いと思うんですよねえ。

もちろん、今でも、固定長とか、短縮表記が、意味のある世界もあるだろうけど、可変長の自然言語の表記、というのが、基本トレンドでしょう。

ソフトウェア技術、ハードや通信のコストからすれば「固定長・短縮表記」は、はるか昔の話し。
今は「可変長で、自然言語の冗長な表記」を扱うのに、ハードの制約もないし、プログラミングも、便利なツールがたくさん揃っている。

もっと、自然言語に近づけようよ、ということ。
DSLの議論なんかも、こういうトレンドの一つでしょう。

まあ、技術者のニーモニック好きは、そう簡単にはかわらないかもしれない。
ニーモニック好きの技術者が、ニーモニック連発の言語使って、「ドメイン固有の言語」の議論しているのは、なんか、滑稽に見える。(まじめに取り組んでいる方には申し訳ありませんが)

ドメイン固有言語は「自然言語」でしょう、やっぱり。

自然言語に近い名前空間を設計して使おう


xml の名前空間とか、URI とかは、問題領域の言葉をそのまま使った、名前空間に設計できるならそれが、一番わかりやすい。

ドメイン層のクラス名やメソッド名、テーブル名やカラム名も、全部、そう。

「識別キー」の設計は、「番号体系」や「ニーモニックコード」の設計よりも「自然言語に近い名前空間の設計」にこだわり、重視するのが、良い設計なんだと思う。

「自然キー」で識別するのが、もっとも「自然」 【パターン】

「自然キー」は、データモデリングの用語からの借り物です。
データモデリングの世界では、システム内部で機械的に発行する「人工キー」との対比で使われる用語。

「レコードを一意に識別するキーの候補には "自然キー" と "人工キー" がある、というように使われる。

自然キー( natural key ) は、名前、電話番号、メールアドレスなど、人間が使っている情報。
人工キー( surrogate key ) は、システム的に生成した番号。人間にとっては、意味のない情報。

人工キーは、人目に触れない、内部限定、というのが本来の意味らしい。

ここでは、「人間が使っている識別情報」を自然キー、システムで機械的に作成した番号を、人工キーとしておきましょう。

ソフトウェアのモデリングと設計・実装は、「自然キー」を大切にすべき。

自然キーは主キーとしては扱いにくい


データモデリング的には、自然キーで、一意に識別するためには、通常は、複数の属性を組み合わる必要がある。
「同姓同名」が、わかりやすい例ですね。
「同姓同名」に生年月日を組み合わせても、完全に「一意」になるわけではない。

データベースの実装の視点からは、主キー (と外部参照)は、人工キーでやったほうが確実だし、SQL 文の JOIN 句は単純になる。

注文番号とか、契約番号というように、人工キーが、実際の業務で、つまり自然キーとして使われていることもある。

「人」は、人工キーより自然キーを好む


当たり前だけど、人にとって「自然」だから、「自然キー」なんです。
データベース的に、完全に一意にならないとか、多くの属性を組み合わせた一意識別は、扱いにくい、というような議論は、システム内部の議論。

人は、いつでも「自然キー」を好むんです。

「注文」を、注文番号で探すより、注文した人、受注日、受注内容から、「自然」に識別しようとする。

「契約」も、契約番号より、契約の種類と契約相手から探す。

「人」にとって、役に立つ、使いやすいソフトウェアを造るには、「自然キー」を使って仕事がやりやすい仕組みにすべき。

候補の提示と選択


自然キーは、複数の属性を組み合わせて、はじめて、一意になる。

たとえば、郵便番号を特定するには、東京都文京区本郷 という 都道府県+市区町村+町域という三つの属性で一意に決まる。

URI 式だと、 /郵便番号/東京都/文京区/本郷 という形式。

業務上、こういう一意識別できる属性の「完全」な組み合わせを使うことよりも、不完全な情報、一部の属性だけで探したいことがはるかに多い。

郵便番号の例だと、「本郷三丁目」の郵便番号は、という感じ。本人は、そこが、東京都で文京区であることを、無意識に前提にしている。 明確に意識する属性は「本郷三丁目」。 これで、一意に識別できると漠然と期待している。
(実際に、一意に識別できてるかもしれない)

自然キーは、一意に決まるとは限らないので、「候補」を提示して、選択する、というのが自然で使いやすいと思う。

ジョルダンの乗換案内の駅名入力は、ajax を使って、一意でない場合の候補の提示を自然にやろうとしている。

もっとも、「北名古屋」とか、実際にはない(?)駅名を入れると、怪しげな選択リストになるけどね。

ajax のように、あまり凝ったことはしなくても、

一覧表示 → 詳細表示、というように、「候補」を提示して、人間が選択、というのは、アプリケーションの基本パターンですね。

URI 方式でも、

/郵便番号 で、都道府県の一覧
一覧から都道府県を選ぶと、市区町村の一覧
一覧から市区町村を選ぶと、町域の一覧
一覧から町域を選ぶと、郵便番号の一覧

というように、「候補の提示、人間の選択」というのが基本パターン。

注文だったら、当日の注文一覧から選択とか、特定顧客企業の注文一覧から選択。
会員検索なら、姓で検索した一覧から、選択。

こういうパターンが自然だし、実際に多い。

自然キーを、テーブルインデックスとして実装する


自然キーは、最初に書いたように、テーブル実装で、主キーになることは少ない。

でも、人間が、自然に識別に使う属性(カラム)は、テーブル設計で、主キー意外に考慮すべきことがある。

それは、「インデックス」の作成。

インデックスは、開発の後半で、性能チューニングのために作成するものではないです。

人間が識別に使いたい情報を、把握して、テーブルの「インデックス」の設計・実装に反映させる。

画面デザインで、カテゴリー式のナビゲーションや、検索条件の指定などを議論したら、それは、そのまま、カテゴリーID カラムのインデックスや、検索対象のカラムのインデックスの作成に直結する、ということ。

利用者が、業務の中で、識別の手がかりにしようと思っているカラムは、インデックスを作成すべきです。

姓で検索することが多いので、「姓」カラムは、インデックスを作成する。
名(単独)で検索することは、ないので、「名」カラムは、インデックスは、作成しない。

こういうのがインデックス設計の基本だと思う。

文脈から絞り込む


人は、文脈、前後関係から、うまくスコープを絞って、少ない情報で一意に識別している。

「中村」さんは、たくさんいるけど、「この会社」とか、「最近注文した」という条件をつけて、「中村」で一意識別している。

ソフトウェアも、こういう自然なやり方を、サポートする仕組みを組み込む努力をすべきだと思っている。

中村さんの注文、という問合せがあったとき、可能であれば、文脈を判断して、特定してあげたい。

Google 検索に、 "I'm Feeling Lucky" ボタンがありますね。
あれは、文脈を判断してくれるわけではないだろうけど、「一発で目的にたどり着く」ことを狙った、おもしろい仕掛けだと思う。

もっとも、私は最近は、あのボタンを使ったことはないですねえ。
一発でたどり着けないことが多い。
それから、一覧自体が「関連情報」として価値があることが多いので、最近は、 I'm Feeling Lucky ボタンがあること自体、忘れかけている。

「関連情報」とか、「安心感」という意味では、「文脈から判断して、一意に特定できたら直接詳細画面へ」という設計は、よくないかも。

一件だけでも、「一件だけだった」という情報を、一覧画面で見せるべきかな?
それが、目的の情報だったら、それを選ぶ。目的の情報じゃなかったら、検索のやりなおし、という設計のほうが、自然かな?


API とか、システムが相手の場合


人間が、会話的に利用する時は、

・部分的な識別情報の入力
・候補一覧の提示
・人間が選択

という流れが自然だと思う。

API による情報提供とか、メッセージングなどのアプリケーション連係だと、そうはいきませんね。

一意に識別できなかった場合は、たぶん、エラーとして扱わないといけない。
データベースの主キーでの操作と同じ世界になる。

API とか、メッセージングの場合の識別キーとしては、データベースと同じように、人工キー、つまり、機械的に生成した一意識別用の番号を使うという方法がある。

もうひとつは、URI のフルパスを指定する、という方法。
個人的には、最近は、URI のパス指定方式が良いのかな、と思っている。

URI で、記述的に、

/銀行口座/東京銀行/本郷支店/普通/223344

という指定の方が、

<bankaccount id="89034933482" />

より、良い感じだなあ、と。

目的にもよるけど、自然キーと人工キーの両方を記述するほうが、実用的かな?

問題は、公開する API とか、外部のアプリケーションとの連係とかで、意味のある「ID」設計は、難しいということ。

書籍のように、そのドメインで、ISBN という ID 体系が確立していれば、それを使える。

でも、私たちの問題領域(転職サポート)では、「求人」や「応募者」の ID (識別キー)を、アプリケーションや企業をまたがった、共通の識別体系をつくるのは至難。

そもそも、単独のサービス内でも、「重複応募」で、同一人物の特定、という問題がある。
「求人」も、内容の変更や、再掲載ができるので、何を持って「同じ求人」と捉えるかも問題。

なんか、いいアイデアないですかねえ。

個人の識別キーの設計 : 名前? 番号? メールアドレス? 別名?

顧客や申込者の「個人」を識別するキーの設計は、なかなか興味深いテーマ。

分かりやすさでは「名前」。一意識別性なら「番号」。
ドメイン名や、メールアドレスは、「名前」の分かりやすさと、一意識別性を兼ね備えている。

宅配ピザ


宅配ビザに注文すると、電話番号を質問される。
顧客データベース上で、私(の自宅)を電話番号で特定しているわけだ。

データベースに登録されている住所(と名前も?)を、確認されて、顧客の確認を終了。
これで、どこに配達するか、特定できたわけだ。

簡易な識別方法として、電話番号が有効なことは多い。

宅配ピザであれば、国番号とか、市外局番も不要。東京だと、4桁-4桁の番号が、リピート顧客の識別キーになる。

利用する側も、受付側も、負担が少ない、良い設計だと思う。

クレジットカードの問合せ


クレジットカードについて、突然、使えなくなったので、コールセンターに電話。

カード番号を聞かれた後、登録済みの「住所」「電話番号」「生年月日」を質問されて、本人確認。
結構面倒。まあ、宅配ピザというわけにはいかない。

限度額オーバーだった。

ちゃんと、限度内でやっているつもりだったけど、海外で使った金額が、前月引き落とし済みと思っていたら、遅れて、今月の請求になっていたので、限度額オーバーになっちゃった。

でも、この情報が、さきほどの本人確認で聞き出せるのは、ちょっと、不安。

カード番号、住所、電話番号、生年月日、悪意があれば、他人が私に成りすますのは、それほど、むずかしくはなさそう。

もっとも、金額とかのナマのデータは、オペレータは、一切、しゃべらない。
(たぶん、画面でも、この段階では、そこまで見れない)

「限度額オーバー」という状態だけ教えてくれる。
セキュリティレベルと、問合せの利便性を考えると、ここらへんもバランスがとれた仕組みになっているのかもしれない。

何のときだったか、忘れたけど、「登録してある銀行口座番号」まで、聞かれたことがあった。
クレジットカード会社からの請求書とかには、口座番号は、マスキングしてあるので、この情報まで、私がちゃんと提示できれば、より確かな本人確認になるわけだ。

リフレクソロジー


時間があいたので、足裏マッサージでもしようかと、リフレクソロジーのチェーン店に飛び込んだ。

会員かどうか聞かれたので、会員と申告。でも会員カードをもっていなかった。

誕生日(月と日)を聞かれて、苗字を聞かれて、本人確認終了。
同じ誕生日で、同姓の人はいないみたい。

あとは、支払い時に、ポイント加算してくれたことを告げられる。

誕生日(月と日)で、ハッシングして、そのグループ内で、姓で特定にいく。
同姓同名の場合、どうやっているのか、ちょっと興味があるなあ。
(お店の人とか、この記事読んでいたら、教えてくれないかしら)

応募者の重複チェック


私のメインの活動領域、人材の募集と応募のドメインでの「個人の識別」のテーマ。

同じ人が、繰り返し応募してきたり、別の職種に同時に応募する、というのはよくある話。

で、応募情報から「同一人物」を特定するのが、結構やっかい。

メールアドレスとか、電話番号は、わりと、変わるんですよね。

いちおう、「生年月日」と「ヨミガナ」をキーにして、「もしかして同一人物」リストを出すようにしている。

漢字の姓名は、表記の揺らぎが意外とあるんですよね。(大沢さんと大澤さんとか)

最後は人間が判断


どのケースも、「本人確認」とか「同一人物」判断は、人がやっている。

クレジットカード会社とかは、情報の一致だけじゃなく、相手の応答がつまったり、へんだったりしたら、マニュアルが、しっかりしていて、質問レベルが難しくなるようになっているらしい。

そうそう、何か、住所か何かを言い間違えた時に、えらいめにあった気がする。

管理番号


システム化の王道(?)は、やはり、管理番号をつけて、一意に識別すること。

これは、電子化以前から、業務の仕組みとして、確立しているやり方。

顧客番号、注文番号、契約番号、請求番号、...

紙伝票に手書きの時代から工夫されている。

手書き時代の番号は、「年」とか、「月」を先頭につけて、それ以下を連番にする、というのが多かったような気がする。

これ、システム的にはちょっと厄介なんですよね。月ごとに、連番をリセットするというのは。

あと、最近は、分散処理というか、ネットワーク上で、複数のサービス(異なるアプリケーション)から、顧客登録が可能、というケースもでてきた。

この場合は、一意に識別する顧客番号を、発行するのは、結構たいへん。

まあ、先頭に、サービスごとの識別番号をつけるとか、いうやり方もあるにはある。

年、月、登録サービスなどの情報を、識別キーに埋め込むことは、あまり良い方向ではなさそう。

この程度のレベルであればいいけど、会員種別とか、居住地域とかの情報が、識別番号の一部にまぎれこんでくるのは、明らかに、汚染。

登録制


ドメイン名とか、ISBN は、登録制だから、グローバルに、一意識別が保証されている。
電話番号も、国番号まで使うと、いちおう、そうなっている。

通信インフェースカードの識別番号である、 MAC アドレスも、世の中のすべての通信カードで、一意に識別するようになっている。 (ろくでもない製造業者が、偽造番号で作っていることが、昔はあった。今でもあるのなか?)

JAN コード(商品のバーコードでおなじみ)も、登録制で一意の番号。ワールドワイドの商品識別体系の一部として互換性があり、世界中で一意になる仕組み。

もっとも、JAN コード自体は、一意だけど、同じコードを複数の商品に使ったりしているメーカーがあるので、商品のコードとしては一意ではない。

サイズ、色違い、バージョン違いなどは、 JAN コードでは、特定できないのが普通。

こういう共通の識別体系じゃなくても、アプリケーションで独自に「発番」の仕組みを作って、登録制にするのは、常套手段。

ネットワーク上で、他のサービス(他の識別体系)との連係が、ますます重要になってきているの。アプリケーションの独自の発番の仕組みを考えるときは、外部の識別体系との関係性や、整合性には、気を配るべき。

まあ、URI のように、ドメイン名(サービス名)以下に独自の体系であれば、一意識別は保証できる。
でも、利用者からみたら、同種のサービスは、同じ識別体系であるほうが、使いやすい。

妥当性検証


クレジットカード番号や、ISBN (本の国際識別番号体系)は、単純な連番ではない。
チェックデジットと言って、本体の番号を、一定のロジックで計算した、結果の数字を付加している。
登録データベースに問い合わせなくても、番号だけで、妥当性検証ができるようになっている。
---

とりとめなくなってきちゃったなあ。
とりあえず、識別キーネタ、ということで書いておきましょう。

URI でリクエストされた時に送るデータの設計



URI は、記述的にわかりやすく設計する。
利用者が欲しいと思っている情報を、直感的に表現した名前が良い URI。

/郵便番号/東京都/文京区/本郷三丁目

で送り出す主なデータは、 "113-0031" 。

path の先頭の要素は、「データの集合」の名前にする


関心事は、「郵便番号」だから、Path も、「郵便番号」ではじめる。そして、送り返すデータは、ストレートに、"113-0031"。

こういう単純明快さが、良い、URI の設計。

path の先頭は「データの集合」の名前にするのが原則。

「郵便番号」というデータの集合から、特定のデータを指定するための仕組みが、URI ということ。

ROA ( リソース指向アーキテクチャ)の発想だとこうなる。

アマゾンは、path の先頭を /本-和書/ とか、使っていますね。本-和書についての「データの集合」を起点に探す。

手続き的というか、機能指向だと、 グーグルのトップページのように、/search になる。
もっとも、個別の検索機能では、

/webhp Web のホームページ
/imghp イメージ情報のホームページ
/maps 地図

という感じで、対象とする「データの集合」の名前が、path の先頭要素になっている。

日本郵便の郵便番号検索のように、プログラミング指向だと、 /cgi_zip/zipcode.php を起点に探すことになる。
アンチパターン。

レスポンスデータの内容


URI の設計がよく出来ていれば、レスポンスデータの内容も、単純に決まるはず。

/郵便番号 であれば、 "113-0031" を返す。
/maps であれば、 指定位置の "地図" を返す。

/本-和書 はちょっと、悩ましい。実際に、アマゾンの場合は、一ページに収まりきらない、膨大なデータが帰ってくる。 私は、ほとんど、目的買いなんで、あんなにたくさんの情報、欲しくないんですけどね。

URI はできるだけ、シンプルで、わかりやすくあるべきだし、返すデータも、できるだけ、シンプルで、わかりやすくするのが良い設計だと思う。

もっとも "113-0031" だけでは、さすがに、シンプルすぎる。

実際の検索サービスでも、”東京都 文京区 本郷 " という 都道府県名、市区町村名、町域名もいっしょに帰ってくる。 町域名は、さらに、「ヨミガナ」も返る。

ページ自体は、パンくずリストがあって、上位の path への リンクとか、いろいろな関連情報へのリンクがてんこもり。

まあ、こういうリンクも、できるだけ目的にそって、厳選して、シンプルにしたほうが良いに決まっている。

アマゾンの本の詳細画面のように、読みきれないほどのデータを、送りつけてくるのは、どうかと思う。

表現の形式


基本の形式は、XHTML 、 XML、 JSON とかかな?

私は、XML がもっとも良いと思っている。データの要素名や構造を適切に表現するには、やはり、XML が一番。

XHTML を、画面レスポンスだけでなく、データのレスポンス形式として、そのまま使う、というアイデアもある。
これだと、画面も、REST サービスも、同じになるので、サーバーの開発側は楽かもしれない。

でも、実際には、 画面を使うシーンで、欲しい情報と、REST サービスで使うシーンで欲しい情報は、違っていることが多い。

だから、XHTML という形式で共通化するメリットを私はあまり感じていない。
クライアント側のソフトウェアを開発することも多いので、 XHTML より、XML でもらったほうが、やりやすいと思っている。

JSON は、手軽といえば、手軽なんだけど、データのメタ情報(項目の名前)とかが、表現されていないので、あまり、良いやり方じゃないと思う。

構造が、 map (連想配列) を使うのが適切な場合は、key の名前で、情報の意味を表現できるかな。

State Transfer ということ


REST は、 ご存知のように、 REpresentational State Transfer から作った造語。

"Representational" が、「表現」ということ。
つまり、「リソース」そのものではなく、その「表現」を返すという発想。

URI にリクエスト内容によっては、異なる表現形式で返すのもあり、というわけだ。
図形的な表現なら、svg のようなベクター形式もありだし、イメージファイル、として表現という手もある。
化学の構造式とかは、XML で表現するボキャブラリセットが、利用されている分野。

注目は、 "State Transfer" のほう。

"State" というのは、状態。 プログラミング的には、変数の集合、データベース的には、カラムの集合ですね。

REST は、リソースの「状態」を、「転送」する、という発想。

状態なんで、もちろん、いつも同じとは限らない。 同じ URI でリクエストしても、そのたびに、違う内容が帰ってくるのが、当たり前の世界。

郵便番号の例だと、 /郵便番号/東京都/文京区/本郷 の URI path で、"113-0033" が変わることはあまりなさそう。

でも、/住所/330-0841 という URI の path だと、昔だったら、「大宮市」が戻ってきて、現在は、「さいたま市」が返ってくる。

「住所」というデータセットは「市区町村の合併」などで、いろいろ変化している。

URI は、「識別キー」だけど、タイミングによって、異なる内容が返ってくる仕組み。つまり、「一意」の識別ではない、ということ。

エンタープライズアプリケーションでは、多くの関心事は、この「変動」する情報。リソースそのものは、 URI で、一意に決まるけど、その「現在の状態」は一意ではない。

「状態の変化」のモデリングと設計のパターン


この変化する「状態」を、うまくモデリングして、設計・実装することが、エンタープライズアプリケーションの中核の課題。

「パターン言語」として、いろいろ、考えていることも、この「状態の変化」のモデリングパターンであり、設計パターンが、いちばんの関心事。

REST の URI/表現のように、「識別キー」を指定すると、「現在の状態」の「表現」を提供する、というのが、まずは基本パターン。

「銀行口座」を指定すると、「残高」を知らせる、というパターンです。

「現在の状態」を提供するためには、「状態を変化」させることが必要。
エンタープライズアプリケーション、事業のためのソフトウェアでは、状態を変化は、誰でも、好き勝手にできるわけではない。

事実を正しく反映しなきゃいけないし、変化させるための、権限とかルールなどの決め事が多いことも多い。
また、「変化」が起きたら、それに反応する必要がある。 「変化の検知」や「変化の通知」も、エンタープライズアプリケーションの重要な関心事。

ここらへんのモデリングパターン、設計・実装パターンをいろいろ書き始めているところ。

URI 設計の例 : 郵便番号と住所

「住所」を例に、関心事オブジェクトの 識別キー(URI) 設計の例。

日本郵便の郵便番号検索を参考にしてみた。
http://www.post.japanpost.jp/zipcode/

URI のおさらい


URI は、ネットワーク上の資源(リソース)を特定するための、

Scheme : "http:"
Server : "//www.post.japanpost.jp"
Path : "/zipcode/"
Query : ?name=value
Fragment : #ページ内のブロックのid

という構造の識別方式。

「知りたいと思っていること」を、この形式で、どうやって、表現するのが使いやすいサービスだろう?

日本郵便の実際のサイトの Path と Qeury を参考にしながら、リソース指向アーキテクチャ(ROA)っぽい、URI 設計にチャレンジしてみる。

path 名は、日本語のほうが、わかりやすいので、今回は、日本語でやってみます。

path のroot


現状は、 /zipcode になっている。

このリソースを要求すると、「郵便番号検索」のメイン画面がでてくる。
よく見ると、2種類のサービスがある。

ひとつは、「住所」から「郵便番号」を探すサービス。
もうひとつは、「郵便番号」から「住所」を探すサービス。

正引きと逆引きですね。

というわけで、そもそも、ルートが2つ必要。

/郵便番号 (これ以下に、郵便番号リソースがどさっとある)
/住所 (これ以下に、住所リソースがどさっとある)

"/郵便番号"を起点に特定していく


「郵便番号」を知りたい、という要求に、日本郵便が提供しているサービスは、「都道府県」と「市区町村」で調べていく方法。

実際の画面では、 /郵便番号 の要求に対して「都道府県の一覧」を、地図とドロップダウンリストで「表現」している。

xml 形式のレスポンスなら、<prefecture id=13>東京都</prefecture>形式の要素を、47個、ならべる。

"/郵便番号" だけで要求すると、レスポンスは「都道府県の一覧」というわけだ。

都道府県まで指定したリクエスト


/郵便番号/東京都

もちろん、レスポンスは、東京都下の「市区町村」の一覧。
さらに市区町村を指定する。

/郵便番号/東京都/文京区

これで「町域」の一覧がでてくる。 ○○町一丁目 というのが多い。
(番地ではなく、「丁目」まで)

郵便番号サービスで、面白いのは、郵便番号が同じであれば「丁目」は表示せず、郵便番号を特定する時に、「丁目」まで意味がある場合は、「丁目」まで表示する。

この設計、どうなんだろう?
利用者は、郵便番号の体系とかルールとか知らないので、町域が「丁目」まであるのと、ないのが混在しているのは違和感がある。

全部「町域」は「丁目」レベルまで、というほうが自然だと思う。

まあ、実際の画面に合わせて、町域を指定する。

/郵便番号/東京都/文京区/本郷

めでたく 「113−0033」にたどり着く。
もっとも、本郷二丁目や三丁目という表示はなく、単に「文京区本郷」だけ。
こちらは、「本郷三丁目」まで、住所を知っていて、郵便番号を探しているので、「文京区本郷」だけの表示だと、正しい郵便番号にたどり着けたか、ちょっと不安になる。

実際の URI


以上のケースの実際のサービスの URI は、

/cgi-zip/zipcode.php?pref=13&city=1131050&id=47742

意味不明。

「URI は記述的にわかりやすく!」に徹底抗戦した見事なアンチパターン。

まず、cgi-zip から、始める。 cgi という仕組みから辿れ、というわけだ。

pref=13 が、呪文のはじまり。まあ、都道府県のJISコードなんだけど、意味は不明。
php という、プログラミング言語まで、指定するのが、郵便番号を知るための、お約束?

city=1131050,id=47742 になると、なにをかいわんや。

画面で誘導するからいいや、ということではなく、やっぱり、URI は、「記述的」で「わかりやすく」しておきたい。

/郵便番号/東京都/文京区/本郷三丁目

ってやると、

"東京都文京区本郷三丁目の郵便番号は、 113-0033 です”

という、設計だと、わかりやすでしょ?

良い URI 設計というのは、こういうものなんだと思います。

"/住所"を起点に特定していく


郵便番号から、住所を調べる場合。

実際のサービスは、テキストフィールドで、「最低3ケタ、最大7桁の数字」を入れるという仕様。

113-003 と、最後のひとけたを入れないと、113-003* に該当する住所がずらっとでてくる。まあ、こういう検索が意味があるのか、私にはよくわかりません。

郵便番号は、999-9999 という3ケタ−4ケタという構造になっているんだから、この構造を素直に表現したほうが良いと思う。

"/住所" だけだと、上の3ケタの一覧がレスポンスされる。

/住所/113 だと、 113 以下の 下4桁の一覧が戻る。

/住所/113/0033 だと、「文京区本郷」にたどり着く。(一つだけ戻ってくる)

まあ、この郵便番号だと、本郷までしか特定できないんだけど、本郷の「丁目」一覧がでてくるほうが、自然だと思う。

郵便局の受け持ち範囲が「本郷」なのは、わかるけど、普通の人は、「郵便局の受け持ち区域」という発想では、暮らしていない。

「郵便番号」関連のサービスを使う時でも、利用者の関心事には「郵便局の受け持ち区域」なんて、持っていない。

ここらへんをもっと気を配ると、サービスのレベルが改良できるんだろうけどなあ。

ちなみに、実際の URI は、

/cgi-zip/zipcode.php?zip=113-003&x=29&y=20

意味不明ですね。 cgi や、 php に関心があるわけないですよねえ。普通は。

google の場合


google も、郵便番号とか、住所で、検索できますね。

http://maps.google.com/maps/geo?&q=1130033
とか
http://maps.google.com/maps/geo?&q=東京都文京区本郷
とか

やると、どんな表現がかえってくるか、興味があるひとは試してみてください。

URI 設計の例としては、query 式なので、あまり興味がわかないけど、レスポンスで戻ってくる内容は、なかなか興味深い。

内容は、一般の人の関心事ではないと思いますが、この API は、どんな人たちのどんなニーズを想定しているかを考えてみるのも面白いかも。

calendar
     12
3456789
10111213141516
17181920212223
24252627282930
31      
<< March 2024 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM