-DTO

http://d.hatena.ne.jp/higayasuo/20060614#1150257533

私は、ドメインモデルは、ドメイン層でのみ使い、プレゼンテーション層では、専用のプレゼンテーションモデルを使うべきだと思っています。なぜなら、ドメイン層とプレゼンテーション層では、モデルとして必要な構造・役割が異なるからです。

ひがさんがいいことを書いてますが、何かあったのでしょうか。さて、内容はプレゼンテーション層のモデルはプレゼンテーション層に限れということですが、私もそう思います。理由は同じ。で、どういう風にするかの私の好みはモデルはイベントハンドラと一緒にしないこと。モデルはSESSIONスコープにて複数の遷移ページで共有できるほうが直感的かなと思うためです。
プレゼンテーション層には、DTOの実装起源のロジックが多数あります。バリデーションなどがそうですしループの管理などもDTO実装起源でウザイナー続出なのです。それ以前にDTOに対しては宿命的に型変換とリフレクションが多発します。リクエストパラメータが名前+文字列値なのをDTOJava型にあてはめたり、ビューとDTOバインディングするのにその逆があったりして。。。Strutsがこのへんのイディオムをフレームワークに仕立てて広めて以来、TapestryもClickもStripesもWicketもEchoもJSFでさえも概ね同じ技術基盤の上に立っています(と思う。意見クレクレ)。ということでもうひとつの進化の可能性にチャレンジ。プレゼンテーション層のDTOを極限までシンプル(POJI)にした上で、コードジェネレーションでDTOにウザイナー機能を自動実装しちゃうのはいかがでしょうか。Ikushipeはそういう仕組みにデザイン中です。
分離されたイベントハンドラはページ指向のほうがいいように思います。アクションパターンと違って、作らなきゃならんブツが減るように思うのですね。動線を押さえるアクションパターンに比して、出所着所を押さえるページパターンのほうが、もちろんアプリケーションによりますが、シンプルなつくりになるようです。さらにそれらがシングルトン徹底でインスタンス数も抑えられます。
まさにデュアル・スピリチュアルですね。。。ってネタですw。娘に付き合って日曜の朝っぱらからTV三昧を強いられています。

package org.ashikunep.ikushipe.sample.calc;
import org.ashikunep.ikushipe.ErrorMessageTo;
import org.ashikunep.ikushipe.ModelScope;
import org.ashikunep.ikushipe.PageModel;
import org.ashikunep.ikushipe.validator.LongRangeValidator;
@PageModel(ModelScope.SESSION)
public interface CalcModel {
    @LongRangeValidator(max=10, min=0)
    @ErrorMessageTo("validationErrors")
    long getNum1();
    @LongRangeValidator(max=10, min=0)
    @ErrorMessageTo("validationErrors")
    long getNum2();
    void setResult(long result);
    void setValidationErrors(String[] errors);
}

DTOはここまで削っても機能はリッチに作れる。Ikushipeがコードジェネレートでがんばってくれます(予定)。

package org.ashikunep.ikushipe.sample.calc;
import org.ashikunep.ikushipe.WebPage;
@WebPage(CalcModel.class)
public class CalcPage {
    public Class<?> calc(CalcModel model) {
        long num1 = model.getNum1();
        long num2 = model.getNum2();
        model.setResult(num1 + num2);
        return ResultPage.class;
    }
}

イベントハンドラの引数は、DIです。こういうのもDIって言うのかは謎ですが。

package org.ashikunep.ikushipe.sample.calc;
import org.ashikunep.ikushipe.MovePageTo;
import org.ashikunep.ikushipe.WebPage;
@WebPage(CalcModel.class)
public interface ResultPage {
    @MovePageTo(CalcPage.class)
    Class<?> back();
}

ページも、POJIでいける場合もあるでしょう。

package org.ashikunep.ikushipe.sample.todo;
import java.util.List;
import org.ashikunep.ikushipe.AccessByIndex;
import org.ashikunep.ikushipe.ModelScope;
import org.ashikunep.ikushipe.PageModel;
@PageModel(ModelScope.APPLICATION)
public interface TaskModel {
    @AccessByIndex(Task.class)
    void setTask(List<Task> task);
    List<Task> getTask();
}
package org.ashikunep.ikushipe.sample.todo;
import java.util.Date;
import org.ashikunep.ikushipe.ModelScope;
import org.ashikunep.ikushipe.PageModel;
@PageModel(ModelScope.INTERNAL)
public interface Task  {
    void setDescription(String description);
    void setRegistDate(Date registDate);
}

ループまわすときはこんな感じ。DTOの中に明示的(INTERNALスコープ)な子要素DTOを持つ。
で、このループ要素にはJSFにたとえるなら、#{ task(5).description }、みたいな風にアクセスさせようかなと。インデックスアクセスのための、TaskModel#getTask(int index):Task はやはりコードジェネレートで。インデックスアクセスのために元プロパティ型がjava.util.Listと配列に限られてしまいIterableなだけのヤツは無視してしまいますが、それでいいのだ(と思う)。プレゼンテーション特化のDTOなのだから。