最近、自分のやろうと思っていることがうっすら見えてきたように思う。
おおまかと言うと、「プログラムによる処理とは、プロパティの操作である」と単純化する事で得られる利点を最大限利用して、楽をしたり、DRYにしたり、責務を適切に分散させたりしようという魂胆だ。プログラム全体を閉じたプロパティ操作の系と看做して統一的に取り扱えるようにしたい。
もちろん一般的な処理の中身はプロパティ操作だけではないというか、プロパティ操作として表現できるのは限られたほんのごく一部でしか思うけど、プロパティ操作だけに限定した場合でも結構出来ることは多いし(URLを指定してDBから値を取り出してHTMLで表示したりとか)、それ以外の場合も適切にメタ情報を吐き出すことでプロパティ操作系に参加させて恩恵を得ることは出来ると思う。
以下適当に関係ありそうなことをとりとめもなく書いておく。ほぼ自分用のメモなので内容は分かりにくいかも。
データ操作
検索と永続化は筋が違うと思うので、とりあえず検索について考えたい。よくあるDBを使った検索って、プロパティ間の関係を指定してグロスのデータからその一部を取り出すってことだと思う。データの中身もプロパティの組だ。
あくまで表現の話であって、あんまり具体的なストレージ(RDBMSとかKey Value Storeとか)に限定される話ではない。
プレースホルダーなんかもDBにあるテーブルとローカルのオブジェクトのJOINとして表現できる(具体的にどう動かすかはともかく表現の話ね。) というか3年ぐらい前に仕事で書いたO/R Mapperはそういう風に書いた。
あと、制限(selection)はべき等だとか、「(Q1 AND Q2)の問い合わせ結果」⊆「Q1の問い合わせ結果」とか、そういう性質を使って問い合わせを束ねたりキャッシュを効率化したりできるはず。
関連するterm: SQL(構造化問い合わせ言語)とかDとか
リソース/サービス
いきなり具体例で考えるけど、日記のコメント一覧機能があるとしよう。日記毎にコメント一覧を表示するには、日記のIDで絞り込みして検索するし、その日記に新たにコメントを追加する場合は、追加するコメントに日記のIDを埋め込んで保存するわけですよ。表示時の「日記のIDで絞り込む」と挿入時の「日記のIDをセットする」って違う処理に見えるけど、意味としてはおんなじだよね。つまりこれを二カ所に別々にコーディングするとDRYじゃなくなる。
日記にコメントが従属するという、リソースについての単一の記述を元に、勝手に参照時に日記IDを含むクエリを組み立てて、勝手に挿入時に日記ID付与するようにするのが自然だと思う。
関連するterm: ActiveRecordとか?
URL、というかページ遷移
また具体例だけど、なんらかのデータの一覧ページと詳細ページがあるとして、一覧ページの表示の際に詳細ページへのリンクを表示する場合を考える。個々の要素を表示する詳細ページへのリンクを出力するってことは、つまりリスト内の個々の要素(リソース)→URLの変換をするわけだ。
で、逆に、そのURLでアクセスすると詳細ページを表示するってことは、アクセス時にURL→リソースの変換をするってことだ。
リソースとURLの自然な対応付けが可能なら、プログラマはURLの中身を知る必要はなくなるはず。リソースを特定する情報が全部入ってればフォーマットは気にしなくてもいいので。
あるデータに従属するデータを別のページで表示するということを考えると、データをプロパティで辿って行く操作と、ページをリンクで辿って行く操作が、データ(リソース)→URLのマッピングによって対応づけられる。準同型ってやつになるのかな?
もっとも実際には一つのURLに複数種のリソースが同時に対応づけられてたり、逆に同一のリソースが複数のページで表示されたりするんだけど、その辺も踏まえてうまく定式化したい。
複数種のリソースを一つのURLに押し込めた例として、「元のページに戻る」遷移がある。従属関係にないデータの場合は、現在表示しているリソースとは別に、元のページのリソース(を特定する情報)も憶えておかないといけないのだけど、「パラメータ○○でページ間を持ち回して〜」とかアドホックに考えるんじゃなくて、複数リソースにマッピングされたURLと考えればすっきりすると思う。
あと、情報を保持するのはURLじゃなくてもCookieでもWebアプリサーバのセッションでも良い。リソースとの境界で全部そろってれば良くって、リソースそのものがWebに露出している必要は無い。
関連するterm: RESTとかURITemplateとか
HTMLテンプレート
HTMLテンプレート上でデータの中身を取り出して表示するのにプロパティによる指定を使うわけだけど、単にレンダリング時に値を取り出すだけじゃなくて、いろいろ再利用できないかと思っている。前に書いたこと(「ついでなのでアイデアメモ - terazzoの日記」)と被るけどもう一度書くよ。
HTMLテンプレート上でのプロパティの使い方をきちんと定式化できれば、型推論を取り入れて、編集時にチェックしたりIDE上で補完したりできるかも。
あらかじめHTMLテンプレートの中身をスキャンすることで、DBから取得するデータを最小限にしたり、遅延ローディングをおこさず先読みしたりできるかも。
HTMLの部分部分とデータの依存関係を調べることで、自動でパーシャルキャッシングしたり事前に部分適用(簡約)したりできるかも。
突き詰めるとテンプレートの構造をスキャンしてデータフローダイアグラムを取り出す、みたいな話になるかも。
関連するterm: PFTLとかスライシングとか
パイプライン
DIコンテナの設定ファイルって静的なオブジェクトグラフの構築を外部ファイル化したものと考えることができると思う。但しSeasar2の場合はもう少し進んでいる。
Seasar2には自動バインディングという機能があって、コンテナ内でオブジェクトを生成する時に、生成するクラスのコンストラクタやセッターなどと型が適合するコンポーネントが既にコンテナに登録されていれば、それを自動でインジェクトしてくれる。グラフ全体を明示的に書くんじゃなくて、型情報を元に適合する型間で自動的にグラフが作られると考えることも出来る。
DIコンテナで作られるオブジェクトグラフというのは、あくまでコンポーネント間の静的なものであって、実際の処理の流れとしては、インジェクトされた側からインタフェースを通じて能動的に呼び出さないといけない。例えば、なんらかのサービスオブジェクトに「認証コンポーネント」がインジェクトされているとして、リクエストからIDとパスワードを取り出して認証コンポーネントの認証メソッドを呼び出す、などの処理は利用する側で明示的におこなう必要がある。
DIの受け身根性をさらに徹底するなら、起動時に認証コンポーネントがインジェクトされるんじゃなくて、リクエスト時に認証済みのユーザオブジェクトをインジェクトしてくれないかな、と思う。言い換えるなら、個々のコンポーネントの入出力の型情報を元にパイプラインを自動で組み立ててくれないかというイメージ。
自分の過去のエントリで言えば「それWebL○gicで出来るよ? - terazzoの日記」の最後の図みたいなのを自動で作る感じ。
ヴァリデーション
入力チェックについては前からとても悩んでいて、フォームの文字種チェックとビジネスロジック上の内容のチェックは筋が違うんでないかと思いつつも、でも処理としては一カ所でやってしまいたいと考えていた。
ビジネスルール上のチェックと、サイズなどDBの制約に起源を持つチェック(これもビジネスルールなんだけど)と、「パスワード」と「パスワード(確認用)」の一致確認みたいなプレゼンロジック的なチェックと、数値フィールドの文字種チェックみたいな表現上のチェックは、全て異なる責務に基づくもので、それを一カ所に全部書くのはおかしいし非効率だしDRYじゃないだろ、と思っていた。
よく考えると、これってデータベースへの問い合わせについて考えていたこと(前回参照)を、丁度逆方向にしたものなんだよね。ということはその考え方も応用できるはず。
つまり次のようになる。各モジュールが担っている責務毎に、ヴァリデーションというものは存在する。個々のレイヤーでチェックをおこなっても良いんだけど、各モジュールは情報だけを提供して、実際の処理は一番フロントで実行しても良い。責務は分割、処理は一カ所で。となる。
ところで、実際にロジック層に処理が届いた時に、その入力が上層でチェック済みかどうかはどうやって確認すれば良いだろう。JTAのトランザクションみたいに、呼び出し全体を通じて個々のプロパティが入力チェック済みかどうかを保持するコンテキストを(ThreadLocalとかで)用意するというのが良いかも。
そうすると、プレゼン層のパラメータとロジック層でのプロパティ(オブジェクトのフィールドなど)の対応付けってどうするのよ、という問題が出てくる。プログラム上で値の伝播が追えないなら、各レイヤーでメタ情報を吐き出すようにする必要があるかも。具体的に言えば。
diaryCommentDto.setMessage(diaryCommentForm.getMessageString());
というようにプログラムに直書きされていると手に負えない(勇気がないだけかもしれないけど。) それに対して、
dxo = new EntityMapping(DiaryCommentForm.class, DiaryCommentDto.class); entityMapping.addPropertyMapping("messageString", "message"); .... dxo.convert(diaryCommentForm, diaryCommentDto);
みたいな感じで処理されていれば、このdxoを再利用してヴァリデーションの情報を共有することはできそう。
全半角変換とか、電話番号1,2,3を連結/分解したりとか、そういう場合にも長さチェックの情報などが自動で変換されるようにする為には、単純に対応付けとかいうだけじゃなくて、いろいろがんばらないといけないかも。
まあ後は、エラーメッセージに出す日本語の項目名をどこで持つのか問題とかもある。