<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE document PUBLIC
  "-//Apache Software Foundation//DTD XDOC 1.0//EN"
  "http://maven.apache.org/dtd/xdoc_1_0.dtd">

<document>
  <properties>
    <title>ドメイン層 - Domain Layer</title>
    <author email="kurokkie@users.sourceforge.jp">kurokkie</author>
  </properties>
  <body>
    <section name="ドメイン層">
      <p>
ドメイン層には United Front 2 の世界をオブジェクト指向によってモデル化したクラス群が配置されます。主要な
ロジックはこの層に集中しており、システムの核（<strong>ドメインロジック</strong>）を形成しています。ドメイン
層のクラスは <strong> オブジェクト指向プログラミング</strong> を最大限活用するために、POJO で設計され
ています。POJO にデータアクセスの機能を持たせることで、シンプルでありながら
<strong>単一責任の原則</strong> を守った凝縮度の高いクラス群となっています。
      </p>
      <p>
ドメイン層のクラスは、システムがどのように振舞うかではなく、システムが対象としている「もの」に着目して設計され
ます。対象となる「もの」の抽象的なデータ型を設計し、その型からきめ細かなメソッド群を導きます。この考え方はオブ
ジェクト指向の原点であり、United Front 2 の開発スタイルはおのずと
<strong>ボトムアップアプローチ</strong> になります。
      </p>
      <p>
ドメイン層のクラスは <code>org.unitedfront2.domain</code> 以下のパッケージに収録されています。以
降はドメイン層に配置される代表的なクラスについて説明します。
      </p>
      <div class="info">
        <h5>Note: なぜオブジェクト指向か？</h5>
        <p>
なぜオブジェクト指向アプローチなのでしょうか？実際のところ、<strong>トランザクションスクリプト</strong> に
代表される手続き型のアプローチを採用しても、同様の機能を持つアプリケーションシステムは構築可能です。むしろ開
発の初期フェーズでは、手続き型アプローチの方が理解しやすく扱いやすいかもしれません。オブジェクト指向アプロー
チの利点は、システムが成長し複雑化していく過程で現れます。オブジェクト指向アプローチで開発されたソフトウェア
は、システム機能（実際にユーザが触れる機能）ではなく、より安定したデータ（ドメイン）を中心に構成されるため、コー
ドの再利用性が高まるのです。一貫したオブジェクト指向アプローチの採用は、コードの重複や矛盾を低らし、高品質な
ソフトウェアを導きます。
        </p>
        <p>
一方で、オブジェクト指向のソフトウェア開発は大変難しいと考えられます。データと振る舞いを適切な粒度でクラスとし
て抽出し、そのインターフェースや継承構造、クラス間の関係を定義しなければなりません。この設計を最初から完成さ
せることはほとんど不可能であり、何度も再利用を試み、修正を加えていく必要があります。この活動に際限はありませ
んが、適度な区切りを付けながら絶えず進めていきます。オブジェクト指向アプローチを採用する際には、まずこのような
考え方をプロジェクトチームに浸透させる必要があります。
        </p>
      </div>
      <div class="info">
        <h5>Note: トップダウンアプローチ vs ボトムアップアプローチ</h5>
        <p>
オブジェクト指向開発により、より再利用性が高く安定したドメイン層を構築できるようになります。システム機能からトッ
プダウンアプローチによって構築したシステムは、ビジネスルールや業務フローに強く依存してしまうため、結果的に柔
軟性や拡張性が低いソフトウェアになってしまう可能性が高いといえます。オブジェクト指向開発では、一旦システム機
能については忘れ、システムが対象としている「もの」に集中します。
        </p>
      </div>
      <div class="info">
        <h5>Note: Java 言語</h5>
        <p>
Java はオブジェクト指向のコンパイル型言語です。Java の能力を最大限発揮できるのは、厳密に型付けされたオブ
ジェクト指向アーキテクチャを採用したときだと考えています。中途半端なオブジェクト指向アーキテクチャでは Java の
能力を引き出せないでしょう。
        </p>
      </div>
    </section>
    <section name="ドメインモデル">
      <p>
ドメインモデルは、振る舞いとデータの両方を一体化させたオブジェクトモデルです。多くの場合、リレーショナルデータ
ベースの１行に対応しています。ドメインモデルは <a href="http://java.sun.com/products/javabeans/">JavaBeans</a>
としての性質を持っており、インスタンス化されたオブジェクト（以下、ドメインオブジェクト）は状態を持っています。ドメイ
ンオブジェクトはデータを保持する性質から、レイヤーを横断して参照され、特に以下のような場面で利用されます。
      </p>
      <ul>
        <li>クライアントからのリクエストのオブジェクト表現</li>
        <li>コントローラ層でのシステム機能の実装</li>
        <li>データアクセス層のインターフェース</li>
        <li>ビュー層でのデータ出力</li>
      </ul>
      <p>
自身が保持するデータに関する全てのロジックを持ちます。これらのロジックでは、データアクセス処理を行うこともあり
ます。データアクセスロジックの実装は DAO パターンを使ってドメインオブジェクトそのものから分離しています。ドメイン
オブジェクトにデータアクセス機能を与えるためには、<code>new</code> 演算子によってインスタンス化した後、ド
メインオブジェクトのデータアクセスオブジェクトプロパティに値を設定（インジェクション）する必要があります。この操作
は後述するドメインファクトリを利用して行います。
      </p>
      <p>
ドメインオブジェクトの <code>transient</code> でないプロパティを、ここでは
<strong>永続プロパティ</strong> と呼びます。永続プロパティはデータベースと同期することを前提としたプロパ
ティになります。例えば、<code>store</code> メソッド実行時にデータベース側で ID が自動生成された場合、
ドメインオブジェクト側の ID プロパティにも値が設定されます。逆に、 <code>transient</code> が付いている
プロパティは <strong>非永続プロパティ</strong> と呼びます。非永続プロパティは、特に注意書きがない限り
永続化の対象になりません。ドメインモデルは、このような性質をよく考慮した上で設計する必要があります。
      </p>
      <div class="info">
        <h5>Note: ドメインオブジェクトとデータアクセス</h5>
        <p>
ドメインオブジェクトにデータアクセス機能を持たせる設計は、単一責任の原則を守るために必要です。データにアクセス
できないドメインオブジェクトでは、必要なロジックの多くをデータアクセス可能な外部クラスに任せざるを得なくなり、責
任範囲が拡散してしまいます。ドメンオブジェクト生成時にデータアクセスオブジェクトを透過的に設定する方法としては、
AspectJ の <a href="http://www.eclipse.org/aspectj/doc/released/devguide/ltw.html">Load-Time Weaving</a>
や Spring Framework の <a href="http://static.springframework.org/spring/docs/2.5.x/reference/aop.html#aop-atconfigurable">AspectJ を使ったインジェクション</a>
などが考えられますが、どちらも一般的でない手法です。そこで、ここでは GoF の <strong>プロトタイプパターン</strong>
を応用した軽量な手法を採用しています。この仕組みの詳細は <a href="#%e3%83%89%e3%83%a1%e3%82%a4%e3%83%b3%e3%83%95%e3%82%a1%e3%82%af%e3%83%88%e3%83%aa">ドメインファクトリ</a>
の項を参照ください。
        </p>
      </div>
      <div class="info">
        <h5>Note: エンタープライズアーキテクチャパターンのドメインモデルとの違い</h5>
        <p>
United Front 2 のドメインモデルは <a href="http://www.amazon.co.jp/dp/4798105538/">エンタープライズ アプリケーションアーキテクチャパターン</a>
で定義される <strong>ドメインモデル</strong> を模範としており、データアクセスをカプセル化することで
<strong>アクティブレコード</strong> としての性質も合わせ持っています。エンタープライズアプリケーションアー
キテクチャパターンと異なる点は、実質的にはデータアクセスロジックを保持していない点です。データアクセスロジック
は <strong>DAO パターン</strong> によって分離しており、<strong>データアクセスオブジェクト</strong>
はドメインオブジェクトの生成時に <strong>依存性の注入</strong> によって設定しています。
        </p>
      </div>
      <subsection name="主要メソッド">
        <div class="smaller">
          <table>
            <tr>
              <th>メソッド名</th>
              <th>説明</th>
            </tr>
            <tr>
              <td><code>toString</code></td>
              <td><code>java.lang.Object</code> からオーバーライド。</td>
            </tr>
            <tr>
              <td><code>equals</code></td>
              <td><code>java.lang.Object</code> からオーバーライド。</td>
            </tr>
            <tr>
              <td><code>hashCode</code></td>
              <td><code>java.lang.Object</code> からオーバーライド。</td>
            </tr>
            <tr>
              <td><code>identify</code></td>
              <td>
引数に指定したドメインオブジェクトが、このドメインオブジェクトと同一であるかどうかを判定します。このメソッドは、
<code>equals</code> メソッドとは異なり、そのドメインオブジェクトを一意に識別できる最小のプロパティを用いて
同一性を判別します。特に説明がない限り、比較には主キーとなるプロパティが利用されます。このメソッドを実装するク
ラスはインターフェース <a href="apidocs/org/unitedfront2/domain/Identifiable.html"><code>Identifiable</code></a>
を実装します。
              </td>
            </tr>
            <tr>
              <td><code>store</code></td>
              <td>
自身を永続化します。永続化の対象は、<code>transient</code> 修飾子が付いていない、全ての永続プロパ
ティです。主キーが設定されていなければ新規のデータとして登録し、主キーが設定されていれば既存のデータを更新
します。永続化の過程で主キーなどが自動生成された場合、自身の対応するプロパティに自動生成された値が設定さ
れます。データに不整合が生じた場合、例外が発生します。このメソッドを実装するクラスはインターフェース
<a href="apidocs/org/unitedfront2/domain/Storable.html"><code>Storable</code></a>
を実装します。
              </td>
            </tr>
            <tr>
              <td><code>delete</code></td>
              <td>
自身をデータベースから削除します。データに不整合が生じた場合、例外が発生します。このメソッドを実装するクラスは
インターフェース <a href="apidocs/org/unitedfront2/domain/Deletable.html"><code>Deletable</code></a>
を実装します。
              </td>
            </tr>
            <tr>
              <td><code>retrieve<i>PropertyName</i></code></td>
              <td>
非永続プロパティをデータベースから復元します。
              </td>
            </tr>
            <tr>
              <td><code>get<i>PropertyName</i></code></td>
              <td>getter メソッドです。</td>
            </tr>
            <tr>
              <td><code>set<i>PropertyName</i></code></td>
              <td>setter メソッドです。</td>
            </tr>
            <tr>
              <td><code>set<i>ClassName</i>Dao</code></td>
              <td>データアクセスオブジェクトを設定します。</td>
            </tr>
          </table>
        </div>
        <div class="info">
          <h5>Note: <code>retrieve</code> メソッド vs レイジーロード</h5>
          <p>
非永続プロパティを復元する他の手法としては、<strong>レイジーロード</strong> が考えられます。レイジー
ロードを使えば、非永続プロパティにアクセスしようとした段階でデータベースへアクセスし、データを復元できます。これ
はデータベースを意識することなく非永続化プロパティを扱える点で便利ですが、逆に言えば予期しないタイミングで
データアクセス処理が発生してしまう危険性をはらんでいます。例えば、トランザクションの境界外のビュー層でデータア
クセスが行われてしまう可能性があります。この問題を防ぐために、ここでは <strong><code>retrieve</code></strong>
メソッドを明示的に呼び出して、データベースアクセスを確実にトランザクション境界内で行う方式を採用しました。
          </p>
          <div>レイジーロードの実装例<br/>
            <pre name="code" class="java">
public void getTransientProperty() {
    if (this.transientProperty == null) {
        this.transientProperty = dao.findTransientProperty(this.id);
    }
    return this.transientProperty;
}
            </pre>
          </div>
        </div>
      </subsection>
      <subsection name="コンストラクタ">
        <p>
ドメインモデルでは JavaBeans の特性の一つであるデフォルトコンストラクタは必須で、必要に応じて引数付きのコン
ストラクタを用意します。引数付きコンストラクタとしては、新規ドメインオブジェクト生成用コンストラクタと既存ドメインオブ
ジェクト復元用コンストラクタの二種を用意しておくと便利です。例はサンプルコードを参照ください。
        </p>
      </subsection>
      <subsection name="インターフェース Domain">
        <p>
ドメインモデルは、自身がドメインモデルであることを示すためにインターフェース <a href="apidocs/org/unitedfront2/domain/Domain.html"><code>Domain</code></a>
を実装します。<code>Domain</code> はメソッドを定義しないマーカーインターフェースです。インターフェース
<code>Domain</code> を実装したドメインモデルは、後述のドメインファクトリからインスタンス化可能でなければな
りません。United Front 2 では、ドメインオブジェクトの生成を Spring Framework の DI コンテナを使って
管理しています。
        </p>
      </subsection>
      <subsection name="静的構造図">
        <p>
次の図は、サンプルのドメインモデル <code>SampleDomain</code> が対応するデータアクセスインターフェース
<code>SampleDomainDao</code> を保持している構造を示しています。
        </p>
        <div><img src="images/domainModel.png" alt="ドメインモデル"/></div>
      </subsection>
      <subsection name="サンプルコード">
        <p>
ドメインモデルのサンプル <code>SampleDomain</code> を示します。<code>SampleDomain</code> は
ID とコード、文字列、浮動点小数をプロパティとして持つ単純なドメインモデルです。メソッド <code>equals</code> 、
<code>hashCode</code> 、<code>toString</code> は <a href="http://commons.apache.org/lang/">Apache Commons Lang</a>
を使って実装を簡略化しています。データアクセスを伴う　<code>store</code> 、<code>delete</code>
メソッドのほか、データアクセスを伴わない <code>multipleTextLengthAndD</code> という固有メソッドを
持っています。<code>SampleDomain</code> の完全なコードは <a href="xref-test/org/unitedfront2/domain/SampleDomain.html"><code>SampleDomain</code> のソースコード</a>
を参照ください。
        </p>
<pre name="code" class="java">
public class SampleDomain implements Identifiable&lt;SampleDomain&gt;, Storable,
    Deletable, Domain, Serializable {

    /** シリアル番号 */
    private static final long serialVersionUID = -2784958436581567548L;

    /** ログ */
    protected final transient Log logger = LogFactory.getLog(getClass());

    /** ID 。主キー。 */
    private Integer id;

    /** コード。一意な値。 */
    private String code;

    /** 浮動点小数 */
    private Double d;

    /** テキスト */
    private String text;

    // 非永続プロパティは transient で宣言
    /** サンプルドメインデータアクセスオブジェクト */
    private transient SampleDomainDao sampleDomainDao;

    /** デフォルトコンストラクタ */
    public SampleDomain() {
        super();
    }

    /** 新規オブジェクト作成用のコンストラクタ。ID は null 。 */
    public SampleDomain(String code, Double d, String text) {
        super();
        this.code = code;
        this.d = d;
        this.text = text;
    }

    /** 既存オブジェクト復元用のコンストラクタ */
    public SampleDomain(Integer id, String code, Double d, String text) {
        this(code, d, text);
        this.id = id;
    }

    // equals, hashCode, toString の実装には非永続プロパティを含めていません
    @Override
    public boolean equals(final Object other) {
        if (!(other instanceof SampleDomain)) {
            return false;
        }
        SampleDomain castOther = (SampleDomain) other;
        return new EqualsBuilder()
            .append(id, castOther.id)
            .append(code, castOther.code)
            .append(d, castOther.d)
            .append(text, castOther.text).isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
            .append(id)
            .append(code)
            .append(d)
            .append(text).toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
            .append("id", id)
            .append("code", code)
            .append("d", d)
            .append("text", text).toString();
    }

    // インターフェース Identifiable の実装
    @Override
    public boolean identify(SampleDomain other) {
        if (id == null) {
            return false;
        }
        return id.equals(other.getId());
    }

    // インターフェース Storable の実装
    // ここではコードの整合性チェックを store メソッドの役割としており、メソッドの例外に
    // DuplicateSampleDomainCodeException を定義しています。
    // コードが他と重複していないことを store メソッドの事前条件とする設計も考えられます。
    // その場合、実行時例外を発生させるような設計にします。
    @Override
    public void store() throws DuplicateSampleDomainCodeException {
        if (id == null) {
            if (sampleDomainDao.findByCode(code) != null) {
                // コードが一意でない
                String message = "The code '" + code + "' already exists.";
                logger.warn(message);
                throw new DuplicateSampleDomainCodeException(message);
            }
            sampleDomainDao.register(this);
        } else {
            SampleDomain found = sampleDomainDao.findByCode(code);
            if (found != null &amp;&amp; !identify(found)) {
                // コードが一意でない
                String message = "The code '" + code + "' already exists.";
                logger.warn(message);
                throw new DuplicateSampleDomainCodeException(message);
            }
            sampleDomainDao.update(this);
        }
    }

    // インターフェース Deletable の実装
    @Override
    public void delete() {
        sampleDomainDao.delete(id);
    }

    // 独自メソッド
    /** テキスト文字数と浮動小数点の掛け算の結果 */
    public double multipleTextLengthAndD() {
        int textLength;
        if (text == null) {
            textLength = 0;
        } else {
            textLength = text.length();
        }
        return textLength * d;
    }

    // getters and setters ...

    // データアクセスオブジェクトは setter メソッドのみを持つ
    public void setSampleDomainDao(SampleDomainDao sampleDomainDao) {
        this.sampleDomainDao = sampleDomainDao;
    }
}
</pre>
        <div class="warning">
          <h5>Warning</h5>
          <p>
同一のドメインオブジェクトを複数インスタンス化した場合、同期をサポートすることが困難になります。ID などによって
一意に識別されるドメインオブジェクトは、一つの処理の中では重複したインスタンスを持たないように気をつける必要が
あります。
          </p>
        </div>
        <div class="warning">
          <h5>Warning</h5>
          <p>
getter および setter メソッドにデータアクセスロジックを含ませることは極力避ける必要があります。getter
および setter はビュー層から何度も呼び出される可能性があり、パフォーマンスやデータの整合性に悪影響を与え
る恐れがあります。
          </p>
        </div>
        <div class="info">
          <h5>Note: 内部表現を暴露しない設計</h5>
          <p>
<a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/util/Date.html"><code>Date</code></a>
型のように、値を素直に setter, setter メソッドで公開してしまうと内部表現を暴露してしまうオブジェクトのプロ
パティは、防衛的コピーを行い複製するようにします。
          </p>
<pre name="code" class="java">
private Date date;

public Date getDate() {
    if (date == null) {
        return null;
    } else {
        return (Date) date.clone();
    }
}

public void setDate(Date date) {
    if (date == null) {
        this.date = null;
    } else {
        this.date = (Date) date.clone();
    }
}
</pre>
        </div>
        <div class="info">
          <h5>Note: コレクションプロパティ</h5>
          <p>
コレクションプロパティの内部構造を外部から変更できないようにするために、コレクションプロパティは
<code>final</code> で定義し、内部の変更処理はメソッド介して行うようにします。また、getter は変更不可
能なコレクションを返します。この設計は永続化プロパティのみが対象で、非永続化プロパティに対しては行う必要はあ
りません。
          </p>
<pre name="code" class="java">
private final Set&lt;String&gt; keywords = new HashSet&lt;String&gt;();

public boolean addKeyword(String e) {
    return keywords.add(e);
}

public boolean addKeywords(Collection&lt;? extends String&gt; c) {
    return keywords.addAll(c);
}

public void clearKeywords() {
    keywords.clear();
}

public boolean removeKeyword(Object o) {
    return keywords.remove(o);
}

public boolean removeKeywords(Collection&lt;?&gt; c) {
    return keywords.removeAll(c);
}

public boolean retainKeywords(Collection&lt;?&gt; c) {
    return keywords.retainAll(c);
}

public Set&lt;String&gt; getKeywords() {
    return Collections.unmodifiableSet(keywords);
}

public void setKeywords(Set&lt;String&gt; keywords) {
    this.keywords.clear();
    this.keywords.addAll(keywords);
}
</pre>
        </div>
        <div class="info">
          <h5>Note: 担当外のデータアクセスオブジェクトは保持しない</h5>
          <p>
ドメインクラスは通常自分が担当し保持しているデータアクセスオブジェクトを持っています。ドメイン間で依存関係を持っ
ている場合などに、他のドメインが担当するデータアクセスオブジェクトを使用したくなりますが、その際はデータアクセス
オブジェクトではなくテーブルモジュールを参照するようにします。ドメインクラスはデータの整合性を保つ役割も持ってい
るため、安全にデータを操作できます。
          </p>
          <div><img src="images/domainDependencies.png" alt="ドメイン間の依存関係"/></div>
        </div>
      </subsection>
      <subsection name="Spring Bean 定義">
        <p>
ドメインモデルは Spring Bean 定義ファイル <a href="http://svn.sourceforge.jp/cgi-bin/viewcvs.cgi/trunk/src/main/resources/applicationContext-domain.xml?root=unitedfront2&amp;view=markup">appilcationContext-domain.xml</a>
に定義します。ドメインモデルは読み込みのたびにインスタンスを新しく生成するよう、スコープを <code>prototype</code> に
で指定しています。
        </p>
<pre name="code" class="xml">
&lt;bean id="sampleDomain" class="org.unitedfront2.domain.SampleDomain"
    scope="prototype"&gt;
  &lt;property name="sampleDomainDao" ref="sampleDomainDao"/&gt;
&lt;/bean&gt;
</pre>
      </subsection>
    </section>
    <section name="ドメインファクトリ">
      <p>
ドメインファクトリはドメインオブジェクトの生成を担当するファクトリクラスです。ドメインファクトリが提供する
<code>prototype</code> メソッドは、ドメインオブジェクトが正しく動作するために必要な初期処理を行った後、
新しく生成したドメインオブジェクトを返します。
      </p>
<pre name="code" class="java">
public interface DomainFactory {

    /**
     * ドメインオブジェクトが動作するために必須となるいくつかの非永続プロパティに値が設定された後の、新しい
     * ドメインオブジェクトを返します。
     */
    &lt;D extends Domain&gt; D prototype(Class&lt;D&gt; domainClass);

    /**
     * ドメインオブジェクトが動作するために必須となるいくつかの非永続プロパティに値が設定された後の、新しい
     * ドメインオブジェクトを返します。返されるドメインオブジェクトには、引数で渡したドメインオブジェクトのプロパ
     * ティが設定されます。引き渡したコピー元のドメインオブジェクトの状態は変化しません。
     */
    &lt;D extends Domain&gt; D prototype(D domainObject);
}
</pre>
      <div class="info">
        <h5>Note: ドメインファクトリパターン</h5>
        <p>
ドメインファクトリクラスを使ってドメインを生成する手法を <strong>ドメインファクトリパターン</strong> と呼ぶこと
にします。ドメインファクトリパターンは GoF の <strong>プロトタイプパターン</strong> を応用した軽量な手法で
す。「<code>new</code> 演算子ではなく <code>prototype</code> メソッドを使う」というのは、United
Front 2 の重要なプログラミングルールとなります。このルールは重要な制約ですが、オブジェクト指向設計を扱い
やすくするという大きな対価をもたらします。
        </p>
        <p>
この設計の欠点は、独自アーキテクチャとなってしまう点です。例えば、ドメインのインスタンス化に <code>new</code>
演算子を使う  O / R マッピングソリューションを採用した場合には不都合が生じるかもしれません。United Front
2 で採用しているような良質のライブラリはドメインオブジェクトの生成に関する拡張点を提供しているため問題ありませ
んが、United Front 2 以外のアプリケーションに本パターンを適用する際には注意が必要です。
        </p>
      </div>
      <div class="info">
        <h5>Note: ドメインファクトリの実装</h5>
        <p>
<code>DomainFactory</code> はインターフェースですので、実装クラスが必要です。United Front 2 で
は、Spring Framework の DI コンテナを利用した <a href="apidocs/org/unitedfront2/domain/SpringBeanDomainFactory.html"><code>SpringBeanDomainFactory</code></a>
を使っています。
        </p>
      </div>
    </section>
    <section name="テーブルモジュール">
      <p>
テーブルモジュールは、一連のドメインオブジェクト群に対し横断的な操作を行うクラスです。多くの場合、リレーショナル
データベースの表と１対１に対応します。テーブルモジュールオブジェクトはアプリケーションの実行中、ただ一つ存在す
る <strong>シングルトン</strong> としてインスタンス化されます。データアクセスのロジックはデータアクセスオ
ブジェクトを使っています。
      </p>
      <div class="info">
        <h5>Note: エンタープライズアーキテクチャパターンのテーブルモジュールとの違い</h5>
        <p>
United Front 2 のテーブルモジュールは
<a href="http://www.amazon.co.jp/dp/4798105538/">エンタープライズ アプリケーションアーキテクチャパターン</a>
で定義される <strong>テーブルモジュール</strong> を模範としています。ただし注意しなければならないの
は、ここでは <strong>ドメインモデル</strong> と同時に使用しているという点です。テーブルモジュールとドメイ
ンモデルの役割は明確に分かれています。一つのドメインオブジェクトに関するロジックはドメインモデルに配置し、複数
のドメインオブジェクトに関するロジックはテーブルモジュールに配置します。
        </p>
      </div>
      <subsection name="主要メソッド">
        <div class="smaller">
          <table>
            <tr>
              <th>メソッド名</th>
              <th>説明</th>
            </tr>
            <tr>
              <td><code>exist</code></td>
              <td>
主キーの値を引数として渡し、対応するドメインオブジェクトが存在するか判定します。存在する場合は <code>true
</code> 、存在しない場合は <code>false</code> を返します。
              </td>
            </tr>
            <tr>
              <td><code>find</code></td>
              <td>
主キーの値を引数として渡し、完全なドメインオブジェクトを取得します。もし対応するドメインオブジェクトが見つからな
かった場合、<code>null</code> を返します。特に明示されない限り、<code>transient</code> となるプ
ロパティには値が設定されません。
              </td>
            </tr>
            <tr>
              <td><code>get</code></td>
              <td>
主キーの値を引数として渡し、完全なドメインオブジェクトを取得します。引数には、存在する主キーを指定してください。
ドメインオブジェクトが見つからなかった場合、例外
<code>java.lang.IllegalArgumentException</code> が発生します。特に明示されない限り、
<code>transient</code> となるプロパティには値が設定されません。
              </td>
            </tr>
            <tr>
              <td><code>set<i>DataAccessObjectName</i></code></td>
              <td>データアクセスオブジェクトを設定します。</td>
            </tr>
          </table>
        </div>
        <p>上記の基本メソッドに加え、次のようなメソッドを持つことがあります。</p>
        <div class="smaller">
          <table>
            <tr>
              <th>メソッド名</th>
              <th>説明</th>
            </tr>
            <tr>
              <td><code>install</code></td>
              <td>
データの初期処理を行います。あるオブジェクトに対して一度だけ実行されます。
              </td>
            </tr>
            <tr>
              <td><code>find<i>PropertyName</i></code></td>
              <td>
主キーの値を引数として渡し、ドメインオブジェクトのプロパティ <i>propertyName</i> の値を取得します。もし対
応するドメインオブジェクトが見つからなかった場合、<code>null</code> を返します。
              </td>
            </tr>
            <tr>
              <td><code>findBy<i>PropertyName</i></code></td>
              <td>
任意のプロパティ <i>propertyName</i> の値を引数として渡し、その値を持った、完全なドメインオブジェクトまた
はそのリストを取得します。もし対応するドメインオブジェクトが見つからなかった場合、<code>null</code> また
は空のリストを返します。
              </td>
            </tr>
            <tr>
              <td><code>findBy<i>PropertyName</i>Like</code></td>
              <td>
任意のプロパティ <i>propertyName</i> の値の一部を引数として渡し、その値を一部に持った、完全なドメイン
オブジェクトまたはそのリストを取得します。もし対応するドメインオブジェクトが見つからなかった場合、
<code>null</code> または空のリストをします。
              </td>
            </tr>
            <tr>
              <td><code>find<i>Condition</i></code></td>
              <td>
条件 <i>Condition</i> を完成させるために必要なプロパティの値を引数として渡し、条件に合った完全なドメイ
ンオブジェクトまたはそのリストを取得します。もし対応するドメインオブジェクトが見つからなかった場合、
<code>null</code> または空のリストを返します。
              </td>
            </tr>
            <tr>
              <td><code>get<i>PropertyName</i></code></td>
              <td>
主キーの値を引数として渡し、ドメインオブジェクトのプロパティ <i>propertyName</i> の値を取得します。引数に
は、存在する主キーを指定してください。値が見つからなかった場合、
<code>java.lang.IllegalArgumentException</code> が発生します。
              </td>
            </tr>
            <tr>
              <td><code>getBy<i>PropertyName</i></code></td>
              <td>
任意のプロパティ <i>propertyName</i> の値を引数として渡し、その値を持つ完全なドメインオブジェクトまたは
そのリストを取得します。引数には、存在する主キーを指定してください。ドメインオブジェクトが見つからなかった場合、
<code>java.lang.IllegalArgumentException</code> が発生します。
              </td>
            </tr>
            <tr>
              <td><code>get<i>PropertyName1</i>By<i>PropertyName2</i></code></td>
              <td>
任意のプロパティ <i>propertyName2</i> の値を引数として渡し、その値を持つドメインオブジェクトのプロパティ
<i>propertyName1</i> の値を取得します。引数には、存在する主キーを指定してください。値が見つからなかっ
た場合、<code>java.lang.IllegalArgumentException</code> が発生します。
              </td>
            </tr>
            <tr>
              <td><code>count</code></td>
              <td>
永続化されているドメインオブジェクトの総数を返します。
              </td>
            </tr>
          </table>
        </div>
      </subsection>
      <subsection name="静的構造図">
        <p>
<a href="apidocs/org/unitedfront2/domain/SimpleTableModule.html"><code>SimpleTableModule</code></a>
はテーブルモジュールの実装を支援する抽象クラスです。<code>SimpleTableModule</code> は整数型の主
キーをただひとつ持ち、継承構造を持たないドメインモデルに対して有効です。この抽象クラスを継承することで、
<code>exist</code> 、<code>find</code> 、<code>get</code> などの基本メソッドの実装を再利用
できます。
        </p>
        <div><img src="images/tableModule.png" alt="テーブルモジュール"/></div>
      </subsection>
      <subsection name="サンプルコード">
        <p>
テーブルモジュールのサンプル <code>SampleDomainTable</code> を示します。<code>SampleDomainTable</code>
はドメインモデル <code>SampleDomain</code> に対応するテーブルモジュールで、基本的なメソッドに加え、
コードから <code>SampleDomain</code> オブジェクトを復元するメソッドと永続化されている <code>SampleDomain</code>
オブジェクトの件数を取得する <code>count</code> メソッドを提供しています。<code>SampleDomainTable</code>
の完全なコードは <a href="xref-test/org/unitedfront2/domain/SampleDomainTable.html"><code>SampleDomainTable</code> のソースコード</a>
を参照ください。
        </p>
<pre name="code" class="java">
public class SampleDomainTable extends SimpleTableModule&lt;SampleDomain&gt; {

    private SampleDomainDao sampleDomainDao;

    // SimpleTableModule の抽象メソッドを実装
    @Override
    protected SimpleFindable&lt;SampleDomain&gt; getSimpleDao() {
        return sampleDomainDao;
    }

    public SampleDomain findByCode(String code) {
        return sampleDomainDao.findByCode(code);
    }

    public SampleDomain getByCode(String code) throws IllegalArgumentException {
        SampleDomain sampleDomain = findByCode(code);
        if (sampleDomain == null) {
            String message = "The sample domain code '" + code + "' not found.";
            logger.warn(message);
            throw new IllegalArgumentException(message);
        }
        return sampleDomain;
    }

    public boolean existByCode(String code) {
        return findByCode(code) != null;
    }

    /** 登録されているサンプルドメインの総数を取得します。 */
    public int count() {
        return sampleDomainDao.count();
    }

    public void setSampleDomainDao(SampleDomainDao sampleDomainDao) {
        this.sampleDomainDao = sampleDomainDao;
    }
}
</pre>
      </subsection>
      <subsection name="Spring Bean 定義">
       <p>
テーブルモジュールはアプリケーション実行中ただ一つ存在するシングルトンオブジェクトで、Spring DI コンテナに
よって管理されます。
       </p>
<pre name="code" class="xml">
&lt;bean id="sampleDomainTable" class="org.unitedfront2.domain.SampleDomainTable"&gt;
  &lt;property name="sampleDomainDao" ref="sampleDomainDao"/&gt;
&lt;/bean&gt;
</pre>
      </subsection>
    </section>
    <section name="検証クラス">
      <p>
検証クラスはドメインモデルと一対一に対応し、ドメインオブジェクトの状態を検証するメソッド群を提供します。検証クラ
スのプロパティは検証データの制約条件として利用されます。検証オブジェクトは主にクライアントからの入力値を検証
する際に利用されます。また、検証オブジェクトをビューへ転送すれば、制約条件を表示させることができます。検証クラ
スは JavaBean ですが、アプリケーション実行中ただ一つ存在する <strong>シングルトン</strong> として利
用されます。
      </p>
      <subsection name="主要メソッド">
        <div class="smaller">
          <table>
            <tr>
              <th>メソッド名</th>
              <th>説明</th>
            </tr>
            <tr>
              <td><code>toString</code></td>
              <td><code>java.lang.Object</code> からオーバーライド。</td>
            </tr>
            <tr>
              <td><code>equals</code></td>
              <td><code>java.lang.Object</code> からオーバーライド。</td>
            </tr>
            <tr>
              <td><code>hashCode</code></td>
              <td><code>java.lang.Object</code> からオーバーライド。</td>
            </tr>
            <tr>
              <td><code>validate<i>PropertyName</i></code></td>
              <td>
プロパティ <i>propertyName</i> の値を引数にとり、正しい値であるか検証します。正しくない場合、
<code>org.unitedfront2.validation.ValidationException</code> が発生します。
              </td>
            </tr>
            <tr>
              <td><code>get<i>PropertyName</i></code></td>
              <td>プロパティの getter メソッドです。</td>
            </tr>
            <tr>
              <td><code>set<i>PropertyName</i></code></td>
              <td>プロパティの setter メソッドです。</td>
            </tr>
          </table>
        </div>
      </subsection>
      <subsection name="検証例外">
        <p>
プロパティが不正な値をとっている場合、<code>validate</code> メソッドは検証例外 <a href="apidocs/org/unitedfront2/validation/ValidationException.html"><code>ValidationException</code></a>
を発生させます。<code>ValidationException</code> は多言語対応したエラーメッセージを保持します。
        </p>
<pre name="code" class="java">
throw new ValidationException("sample.SampleDomain.validation.error", null);
</pre>
      </subsection>
      <subsection name="サンプルソース">
        <p>
検証クラスのサンプル <code>SampleDomainValidator</code> を示します。<code>SampleDomainValidator</code>
の <code>validateCode</code> メソッドは、<code>SampleDomain</code> オブジェクトのプロパティ
<code>code</code> の値を検証します。検証ロジックの実装にはユーティリティクラス <a href="apidocs/org/unitedfront2/validation/Validate.html"><code>Validate</code></a>
を使っています。最後にテーブルモジュール <code>SampleDomains</code> を使ってコードが一意のデータで
あることを検証しています。<code>SampleDomainValidator</code> の完全なコードは <a href="xref-test/org/unitedfront2/domain/SampleDomainValidator.html"><code>SampleDomainValidator</code> のソースコード</a>
を参照ください。
        </p>
<pre name="code" class="java">
public class SampleDomainValidator implements Serializable {

    /** デフォルトのコードの最小文字数のデフォルト (2) */
    public static final int DEFAULT_CODE_MIN_LENGTH = 2;

    /** デフォルトのコードの最大文字数のデフォルト (32) */
    public static final int DEFAULT_CODE_MAX_LENGTH = 32;

    /** デフォルトのコードの正規表現 ([\p{Alnum}]*) */
    public static final String DEFAULT_CODE_REGEX = "[\\p{Alnum}]*";

    /** デフォルトのテキストの最大文字数 (128) */
    public static final int DEFAULT_TEXT_MAX_LENGTH = 128;

    /** シリアル番号 */
    private static final long serialVersionUID = 8383063072303370633L;

    /** コードの最小文字数 */
    private int codeMinLength = DEFAULT_CODE_MIN_LENGTH;

    /** コードの最大文字数 */
    private int codeMaxLength = DEFAULT_CODE_MAX_LENGTH;

    /** コードの正規表現 */
    private String codeRegex = DEFAULT_CODE_REGEX;

    /** テキストの最大文字数 */
    private int textMaxLength = DEFAULT_TEXT_MAX_LENGTH;

    /** サンプルドメインテーブル */
    private transient SampleDomainTable sampleDomainTable;

    @Override
    public boolean equals(final Object other) {
        if (!(other instanceof SampleDomainValidator)) {
            return false;
        }
        SampleDomainValidator castOther = (SampleDomainValidator) other;
        return new EqualsBuilder()
            .append(codeMinLength, castOther.codeMinLength)
            .append(codeMaxLength, castOther.codeMaxLength)
            .append(codeRegex, castOther.codeRegex)
            .append(textMaxLength, castOther.textMaxLength).isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
            .append(codeMinLength)
            .append(codeMaxLength)
            .append(codeRegex)
            .append(textMaxLength).toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
            .append("codeMinLength", codeMinLength)
            .append("codeMaxLength", codeMaxLength)
            .append("codeRegex", codeRegex)
            .append("textMaxLength", textMaxLength).toString();
    }

    public void validateCode(SampleDomain sampleDomain)
        throws ValidationException {
        String code = sampleDomain.getCode();
        Validate.notBlank(code);
        Validate.lengthInRange(code, codeMinLength, codeMaxLength);
        Validate.match(code, codeRegex);
        SampleDomain found = sampleDomainTable.findByCode(code);
        if (found != null &amp;&amp; !sampleDomain.identify(found)) {
            throw new ValidationException("sample.validation.codeUsedByOther",
                    new Object[] {code},
                    "The code '" + code + "' is used by other.");
        }
    }

    public void validateText(SampleDomain sampleDomain)
    	throws ValidationException {
        Validate.notBlank(sampleDomain.getText());
        Validate.maxLength(sampleDomain.getText(), textMaxLength);
    }

    // getters and setters ...

    public void setSampleDomainTable(SampleDomainTable sampleDomainTable) {
        this.sampleDomainTable = sampleDomainTable;
    }
}
</pre>
        </subsection>
        <subsection name="Spring Bean 定義">
          <p>
検証クラスのプロパティはデフォルト値が設定されているため、変更しない限り特別なプロパティの設定は不要です。
          </p>
<pre name="code" class="xml">
&lt;bean id="sampleDomainValidator" class="org.unitedfront2.domain.SampleDomainValidator"&gt;
  &lt;property name="sampleDomainTable" ref="sampleDomainTable"/&gt;
&lt;/bean&gt;
</pre>
        </subsection>

<script type="text/javascript" src="js/dp.SyntaxHighlighter/Scripts/shCore.js"></script>
<script type="text/javascript" src="js/dp.SyntaxHighlighter/Scripts/shBrushJava.js"></script>
<script type="text/javascript" src="js/dp.SyntaxHighlighter/Scripts/shBrushXml.js"></script>
<script type="text/javascript">
dp.SyntaxHighlighter.ClipboardSwf = 'js/dp.SyntaxHighlighter/Scripts/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code');
</script>

<script type="text/javascript">
var ID='100354378-domain';
var AD=1;
var FRAME=0;
</script>
<script src="http://j1.ax.xrea.com/l.j?id=100354378" type="text/javascript"></script>
<noscript>
<a href="http://w1.ax.xrea.com/c.f?id=100354378" target="_blank"><img src="http://w1.ax.xrea.com/l.f?id=100354378&amp;url=X" alt="AX" border="0"/></a>
</noscript>

    </section>
  </body>
</document>
