トップ «前の日記(2013-10-21) 最新 次の日記(2013-10-26)» 編集

日々の破片

著作一覧

2013-10-25

_ WebMVCと設計パターン

WebMVC(面倒なので以降はただのMVC。J2EEのMVCがSmalltalkのMVCと異なるMVCだということは既に10年以上の歴史があるのだから、今更どうでもよろしい)というのは、Transaction Script PatternとDomain Modelの間にまたがるスペクトラムだ。これがMVCの最大の特徴であり利点なのだが、なぜか、Transaction Script PatternとDomain Modelの両極端の声の大きい人が自分の視点を叫ぶ(実際に前者で声が大きい人はいない。彼らは沈黙のうちにコードを広める)。そこで混乱が生まれ、最悪のTransaction Script Pattern実装(貧血)と最悪のDomain Model実装(鬱血 )が幅をきかせることになる。といっても、最悪のDomain Modelは普通は作れないのでそれほど問題はない。

トランザクションスクリプトパターンというのは、コントローラに直接スクリプト(脚本つまり処理の手順)を記述するスタイルだ。想像通りに、コントローラの入り口から出口まで処理が記述される。とはいっても、通常はVにはそれなりに便利な機能があるので直接HTMLを吐き出すことはない(必要ならば吐いても良い)。この場合、Mの仕事はデータベースとの直接的なインターフェイスとリソースの管理となる。たとえば、次の疑似コード(C#とJavaとJavaScriptとRubyのごった煮)はトランザクションスクリプトだ。

void fooBar(request, response) {
    if (request.param[0] ... ) { // 入力をチェックしてみる
        response.redirectTo('error');
    }
    var cmd = (new Model()).connection.createCommand(); // Mはコネクションプールの取得元としてしか使わない
    cmd.query = "select * from tbl where a = ? order by b"; // いきなりSQL
    cmd.params.add(request.param[0]);
    var rows = cmd.executeQuery();
    response.params.add(rows);  // 結果をビューへつなげる
}

これでいいのか? もちろん全然OKだ。
なぜOKなのか? とは考えない。なぜだめなのかを考える。保守性が低い? 別に低くない。見通しが悪い? 全然悪くない。だめな理由は「それはMの責務であって、Cの責務ではない」という教条主義に抵触するというだけだ。くだらない。とはいえ、外部インターフェイスの端点の管理(ここではConnectionの取得、初期化など)はCからは遠ざけておくほうが良い。

で、時が流れたり、いろいろあって、次のようになったとする。

var conn = (new Model()).connection;
conn.startTransaction();
var cmd = conn.createCommand(); // Mはコネクションプールの取得元
cmd.query = "select * ... inner join ... ";
var nextParam = cmd.readScalar();
cmd.query = "update other_tbl set a = ? ....";
cmd.params.add(model.adjustParam2(request.param[2].toInt()));
(さらに3つくらいのテーブルをいろいろいじくりまくり、パラメータをとっかえひっかえしまくる)
conn.commit();

ここに至ってはじめて、それはCの責務ではないと言えば良いのだ。そこで、ごちゃごちゃしたものをMへ移動し、

var model = new Model();
mode.fooBar(request.param[0], model.adjustParam2(request.param[2].toInt()));
reeponse.params.add(model);

ドメインロジックはモデルの責務、とすれば良いだけのことなのだった。

が、それがすべてでも無い。

もし、作成期間が山ほどあれば、最初のfooBarのSQL呼び出しがMにあれば、Mをテストするコードが書きやすい(RequestやResponseのモックが不要だからだ)。なら、Mでやるべきだ。

もし、このコードが仮置きですぐに別の言語/フレームワークなどで書き直す予定だったり、すぐに引っ込める可能性が高いサービスなのであれば、そもそもMにConnection管理機構を持たせる必要すらない。複雑になってきた例ですら、別に問題ない。そのままでOK。メソッドの抽出や分離ができるように考慮することだけが必要なのだ。

MとVとCに分割するメリット(コードの管理、ロジックの整理、拡張や修正を局所化できること、テストの記述が簡単になることなどなどたくさんある)とデメリット(管理すべきソースが増える、単純な処理でもコードナビゲータなしでは読みにくくなる、記述量の増加(特にJavaのようなおまじない系コードが必要な言語だと顕著)、エディターを選ぶために開発マシンのスペックが良くないと辛いとかたくさんある)はプロジェクトどころか、1URIについてですらばらつきがある。

そういうばらつきを無視して、なんでも同じやり方を適用しようとすることこそ避けるべきことなのだ。つまり、本当に必要なのは、MVCそれぞれの責務がどうしたというような特定パターンの知識を頭にたたき込むことではなく、そのプログラムの仕様を実現するには、どうすれば最も効率的(実行効率、保守効率、作業効率、管理効率、検証効率、配備効率などなどのさまざまな切り口について)なのかをさまざまなケースについて独立して考え、実装できることだ。

おしまい。

参考資料 エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)(マーチン・ファウラー)
とはいえ、
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)(Dustin Boswell)
どんなすぐれたデザインもコードが汚ければすべてが意味ない。リーダブルなトランザクションスクリプトパターンのほうが、アンリーダブルなドメインモデルより数1000倍良い。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

ジェズイットを見習え