OOPで見てみよう.オブジェクトの概念は計算の機構として考えられる.プログラマは自分が実現したい情報処理を構築するためにこの機構を利用する.従って,そうして作られたオブジェクトは必ずしも対象実世界のオブジェクト(「何か」)に対応しない.しかし,実世界オブジェクト(対象実世界に想定されるオブジェクトに対応する計算機構としてのオブジェクト)の処理・加工が実世界を対象とする情報システムの本来の役割であり,その一貫性が保持されなければならない.一方,他の計算の都合上作られるオブジェクトはいわばヘルパーであるにすぎない.
実世界オブジェクトを,今後,実体entityと呼ぶことにする.「実体」という語は哲学用語として大昔から使われ,その意味合いも曖昧,かつ多義であるが,ここでは,とりあえず,実世界で人によって認識され,想定される「なにか」を指すものとし,後により具体的に述べていく.
情報システムのユーザ(クライアント)は,実体への作用actionを情報システム(サーバ)に要求する.情報システムはそれに応えて応答する.
この作用は,実体についての情報を,「得る(Read)」,「新たに作成する(Create)」,「更新する(Update)」,「消去する(Delete)」の4種に分類できる.しかし,これを総称してCRUDと呼んだDBでは,作用の対象はDBのデータであって,RDBのようにオブジェクト指向性が乏しいシステムでは本来的に実体との結びつきはない.実世界を対象とする立場に立つとき,DBのデータは実体についての事実を示すものと考えるから,そこではじめて作用対象が実体情報であるとして,実体と結びつく.
このようにCRUDを見直したとき,それぞれの意味合いをより明確にする必要がある.実体は実世界のなかで生まれ,変化し,そして消滅する.情報システムのモデル部(+DB)はこの生涯を反映するものでなければならない.そのうえで,各作用の意味合いを次のように定める.
- Read・・・・指定された時点での実体情報を得る.
- Create・・・実体情報をあらたに作る.
- Update・・・実体の変化を反映するべく,実体情報を更新する.
- Delete・・・実体の消滅を反映するべく.実体情報を更新する.
じつは,この考え方はなんら新しいものではなく,昔から体系が整備された文書では当然のように行われている.たとえば,戸籍謄本.誕生,結婚,死去が記録され,取り寄せればその時点での状態情報を得ることが出来る(それに対し,現在の多くの情報システムは,Updateでは上書きし,Deleteでは文字通り削除してしまう.こうして無数の貴重な記録が消されている).
Create, Update, Deleteはいずれも新情報を送り込むものであり,これをまとめてPutと呼ぶことにする.従って,R3での実体に対する作用はGet(Read)とPutのみであり,PutはCUDに再分類される.
HTTP1.1では,主として通信の効率性の観点から,Get, Put, Delete, Post の4種の作用に整理しているが,R3のPutはこのPutではなく,むしろPostである.すなわち,HTTPのPutやDeleteとことなり,われわれのCUDはいずれもベキ等(idempotent)作用ではなく,2度以上繰り返せば,一回の場合と結果が異なる.ついでにいえば,HTMLにはHTTPのPutとDeleteに対応するメソッドがないから,このベキ等性を活かそうとしても活かせない.さらに,HTMLのGetメソッド自体にはHTTPのGetの規制,つまりシステムに副作用を与えないという規制,はないから,それに付随させるパラメータの解釈次第でたとえば,deleteさえ行わせることがある.実際,初期の(多分現在でも)多くのウェブサイトで同様のことが行われた.このような非一貫性は,効率はおろか,セキュリティなどにも悪影響を及ぼし,寄せ木細工のウェブシステムが起こす混乱の一例となっている.
作用の対象として,個別実体と個別実体の集まりcollectionの2種がある.Getは両者を対象としていて,検索結果の一覧を得る場合は集まりを,詳細を見る場合は個別実体を対象とするのに対し,通常のウェブシステムでは,Putは個別実体のみ対象とする.Railsも例外ではない.そのために,複数個の実体データを追加や更新,削除する場合など,一貫した方式が与えられず,プログラマのアドホック処理に任されている.プログラミング初心者の多くがつまずくところである.R3のPutは両者を対象とすることが出来る.
しかし,話を簡単にするために,個別実体のみを対象とすることにする.個別実体をPutするということは,個別実体の変化をシステムに告知し,その記録を残すことを要求することである.ここで2種類の時間間隔のデータ(メタデータ)が必要になる.ひとつは,実体について事実データが妥当(通用)する期間であり,ひとつはその記録がいつ作られ,いつ(見かけ上)削除されたか,その期間である.前者を[since, till]で表し,後者を[created_at, deleted_at]で表し,事実データと横並びで記録する.R3では,前者をvalid_time, 後者をrecord_time(通常の時変動データベースではtransaction_time)と呼んでいる.
実体の各状態(についての事実データ)をすべて記録として残すと言うことは,自ずから追記型データベース(append only database)にするということである.状態を示す事実データと2種の時間データ(および後述するさまざまなメタデータ),それに行の識別子としてid(これもメタデータ)をつけてRDBでの行columnとする.つまり行はある個別実体のある状態を表す.さらに重要なメタデータとして,肝心の個別実体の識別子を与えなければならない.R3ではこのように変化する個別実体をrunと呼び,その識別子をrun_idと呼ぶ.runはR3が用意するモデル部のRunクラスのインスタンスである.個別実体はこうして,同一のrun_idを持つ一連(英語で言えばrun)の行によって記録され,その各行はその実体の状態をあらわす.
R3が実装する基本的な作用として他に,
- Merge ・・・実体を合併して新しい実体とする.
- Split ・・・実体を分割して複数の実体を作る.
がある.これらもPut作用であるが,先のCUDと異なり,複数の個別実体が関係し,作用を記録するための特殊なメタデータが必要である.それぞれ,merged_toとsplit_fromという名前のメタデータに対し,作用後,作用前の実体の識別子を値とすることでこの作用の記録とする.
Putによる副作用によってシステムの状態自体が変化するが,これらの個別実体の記録によってその変化はすべて記録として残される.したがって,任意の時点におけるシステム記録を見ることが出来.R3のGetの大きな特長となっている.この見る時間をshow_timeと呼ぶ.デフォールトは実時間の現在に設定されるが,それを変更する仕組みが作られていて,設定変更されるとそれ以降は実時間に比例してshow_timeが変動する.比例係数は任意に与えられ,負でも良い.この場合時間の逆戻しになる.しかし,その応用はいまのところ,思い付かない.
show_timeとは別に,個別実体のいつの時点での状態を見たいのか,その時間をassert_timeと呼ぶ.assert_timeは時間間隔としても与えることが出来る.assert_timeはグローバルにも,また局所的にも与えることが出来る.
もひとつ,action_timeがあり,これは作用を実行する時点の時間である.通常は実時間の現在であるが,後述するシミュレーションモードでshow__timeを変更した場合はそのshow_timeに連動する時間がaction_timeとなる.
ここで,時刻を示す定数Past,, Present, Futureを導入しておきたい.Pastは任意の時刻より小さく,Futureはその逆,Presentは実時間現在の時刻で絶えず変動する.プログラミング言語やSQLには時間型があるが,この定数定義に相当するものはいずれもない(そもそも無限大や無限小を定数として持つプログラミング言語はあるのだろうか.あれば非常に便利なのだが).そのため,PastとFutureに人為的な時刻(たとえば,9999年12月31日23時59分59秒)を無理矢理与えなければならず,そうすればそれに伴うトラブルが発生する.こんなところにも実世界を記述しようとせずに計算(数値)の世界のみを見ている現代情報処理の欠点が現れる.
runに対するCUDの各作用に対してDBでは次の操作を行う.
- Create ・・・最初の状態の事実データを記録する行(状態行)を新たに作る.idは行識別子.run_idは便宜上このidに等しくする.sinceとtillはクライアントによって与えられるが,デフォールトとしてsinceはcreated_atと同じ,tillはFuture.created_atはaction_time,deleted_atは未定(null.便宜上Futureを使用している).
- Update ・・・既存のrun記録(同じrun_idを持つ行)に対し,与えられたsince以降の区間(tillをFutureとする)の状態行をDelete(後述)する.続いて,新状態の事実データを持つ行をこのrunに追加する.すなわち,idは新しく,run_idは同じ,sinceとtill, created_atとdeleted_atはCreateと同じ.
- Delete ・・・実行時間(action_time)以降の状態が消滅したことを記録する.すなわち,action_timeからFutureの区間にかかる状態について,(1)まるまるその区間に入る状態のdeleted_atをaction_timeとする.(2)sinceとtillの間にaction_timeが入る状態については,その状態のdeleted_atをaction_timeとし,sinceは同じでtillをaction_time-1(量子化時間)にする状態を追加する(削除しすぎた部分を復活させる).
実体識別子(entity_idと呼ぶ)は,システム全体の処理を通して一貫して参照される.これが前回指摘したオブジェクトを一貫させるための仕組みである.たとえば,ビューでは,対象個別実体のビューをあらわすDOMオブジェクトの属性としてentity_idを持つ(もちろんDOMオブジェクトの標準属性であるid属性を使用してしかるべきだが,CSS表記の技術的問題があって,この非標準の属性を持たせている.これも寄せ集め技術の弊害).
じつは,R3では,個別実体に対し,以上の識別子にさらにそのタイプ(モデルのクラスに対応)を追加して識別子としている.その表記は "#{entity_id}:#{type}"とする.ここで,#{...}は,値を文字列に変換するRubyの表記法.タイプ理論でしばしば使用される表記法である.この表記による実体識別子はR3システム全体を通してユニークであり,様々な処理を通して実体参照に一貫性を持たせることが出来る.
0 件のコメント:
コメントを投稿