<?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 Model Design</title>
    <author email="kurokkie@users.sourceforge.jp">kurokkie</author>
  </properties>
  <body>
    <section name="ドメインモデルの設計">
      <p>
システムの中核となるドメインモデルを設計します。ここでは、「システムが何をするか」といった視点から入るではなく、
「システムが対象としているもの」に着目して開発を進めます。システムの画面イメージなどは描いても良いですが、そ
れはあくまでドメインの設計を手助けするものであり、ここではまだ決定しません。
      </p>
      <p>
まずは全体的な構成を考察します。ブログがいくつかのブログ記事を持ち、さらにブログ記事が複数のコメントを持つと
考え、次のような概念クラス図を描きました。
      </p>
      <div><img src="images/blog.png" alt="ブログ"/></div>
    </section>
    <section name="Blog">
      <p>
それでは、ブログのドメインモデル <a href="apidocs/org/unitedfront2/domain/communication/Blog.html"><code>Blog</code></a>
から考察します。ブログはその所有者やアクセス制御を持つ資源と考えられますから、<a href="apidocs/org/unitedfront2/domain/accesscontrol/AbstractResource.html"><code>AbstractResource</code></a>
を継承することにしました。同時に、このオブジェクトがドメインモデルであることを表す <a href="apidocs/org/unitedfront2/domain/Domain.html"><code>Domain</code></a> 、
永続化可能であることを表す <a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/io/Serializable.html"><code>Serializable</code></a> 、
ドメインオブジェクトを一意に決定できることを表す <a href="apidocs/org/unitedfront2/domain/Identifiable.html"><code>Identifiable</code></a> 、
データベースに保存可能であることを表す <a href="apidocs/org/unitedfront2/domain/Storable.html"><code>Storable</code></a> 、
データベースから削除可能であることを表す <a href="apidocs/org/unitedfront2/domain/Deletable.html"><code>Deletable</code></a>
という基本インターフェースを実装します。プロパティとしては、ブログを一意に表すための「 ID 」と「コード」、「概要」、
および「コメント投稿に対するアクセス制御」を用意しました。
      </p>
      <div class="smaller">
        <table>
          <tr>
            <th>プロパティ名</th>
            <th>型</th>
            <th>説明・解説</th>
          </tr>
          <tr>
            <td><code>id</code></td>
            <td><a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/Integer.html"><code>Integer</code></a></td>
            <td>ID 。このオブジェクトを一意に表すことのできる整数。データベースに登録する前の値は <code>null</code> 。</td>
          </tr>
          <tr>
            <td><code>code</code></td>
            <td><a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/String.html"><code>String</code></a></td>
            <td>コード。このオブジェクトを一意に表すことのできる文字列。ユーザが認識することができ、主にこのブログの URL の一部として用いられる。</td>
          </tr>
          <tr>
            <td><code>overview</code></td>
            <td><a href="apidocs/org/unitedfront2/domain/communication/Message.html"><code>Message</code></a></td>
            <td>概要。概要はブログのタイトルと説明文を持つ。概要の所有者 ID 、参照アクセス権限、編集アクセス権限は、ブログのそれと同一です。概要の著者 ID は所有者 ID と同一です。</td>
          </tr>
          <tr>
            <td><code>ownerId</code></td>
            <td><a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/Integer.html"><code>Integer</code></a></td>
            <td>このブログの所有者のユーザ ID 。<a href="apidocs/org/unitedfront2/domain/accesscontrol/AbstractResource.html"><code>AbstractResource</code></a> から継承したプロパティ。</td>
          </tr>
          <tr>
            <td><code>readAccessControl</code></td>
            <td><a href="apidocs/org/unitedfront2/domain/accesscontrol/AccessControl.html"><code>AccessControl</code></a></td>
            <td>参照アクセス制御。<a href="apidocs/org/unitedfront2/domain/accesscontrol/AbstractResource.html"><code>AbstractResource</code></a> から継承したプロパティ。所有者が自由に選択可能です。</td>
          </tr>
          <tr>
            <td><code>writeAccessControl</code></td>
            <td><a href="apidocs/org/unitedfront2/domain/accesscontrol/AccessControl.html"><code>AccessControl</code></a></td>
            <td>編集アクセス制御。<a href="apidocs/org/unitedfront2/domain/accesscontrol/AbstractResource.html"><code>AbstractResource</code></a> から継承したプロパティ。<a href="apidocs/org/unitedfront2/domain/accesscontrol/AccessControlType.html#OWNER_ONLY"><code>OWNER_ONLY</code></a> で固定です。</td>
          </tr>
          <tr>
            <td><code>commentAccessControl</code></td>
            <td><a href="apidocs/org/unitedfront2/domain/accesscontrol/AccessControl.html"><code>AccessControl</code></a></td>
            <td>コメント投稿に対するアクセス制御。所有者が自由に選択可能です。</td>
          </tr>
        </table>
      </div>
      <p>
ここまでの考察をソースコードに反映させます。この時点で全てのメソッドが実装される必要はありません。getter や
setter 、 <code>identify</code> などの単純なメソッドは実装しておきます。「コード」プロパティは一意な値
でありながらユーザから入力される値でもあるため、コードが重複した際に発生する例外
<a href="apidocs/org/unitedfront2/domain/communication/BlogCodeUsedByOtherException.html"><code>BlogCodeUsedByOtherException</code></a>
を用意し、<code>store</code> メソッドの <code>throws</code> 句で宣言しています。
      </p>
<pre name="code" class="java">
/**
 * ブログを表すドメインモデルです。
 *
 * @invariant ${this.writeAccessControl} is
 * {@link org.unitedfront2.domain.accesscontrol.AccessControlType#OWNER_ONLY}
 * @invariant ${this.readAccessControl} is ${this.overview.readAccessControl}
 * @invariant ${this.writeAccessControl} is ${this.overview.writeAccessControl}
 * @invariant ブログ記事が存在するブログは削除できない。
 *
 */
public class Blog extends AbstractResource implements Domain, Serializable,
    Identifiable&lt;Blog&gt;, Storable, Deletable {

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

    /** ID */
    private Integer id;

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

    /** 概要 */
    private Message overview;

    /** コメント投稿に対するアクセス制御 */
    private AccessControl commentAccessControl;

    // store のドキュメントを長々と書く必要はない。何故なら、Java では JavaDoc も継承されるから。
    /**
     * @throws BlogCodeUsedByOtherException 指定したコードが他のブログで既に使用されている
     */
    @Override
    public void store() throws BlogCodeUsedByOtherException {
        // TODO
    }

    @Override
    public boolean identify(Blog blog) {
        if (id == null) {
            return false;
        }
        return id.equals(blog.getId());
    }

    @Override
    public void delete() {
        // TODO
    }

    // getters and setters
}
</pre>
      <p>
さらに、<code>equals</code> 、<code>hashCode</code> 、<code>toString</code> を実装しま
す。ブログが継承している
<a href="apidocs/org/unitedfront2/domain/accesscontrol/AbstractResource.html"><code>AbstractResource</code></a>
クラスはこれらのメソッドの実装を支援するための仕組みを提供していますので、それを利用することにします。ここでは
<code>toString</code> を実装するために提供される <code>buildToStringBuilder</code> の実
装例を示します。
      </p>
<pre name="code" class="java">
public class Blog ... {

    // ...

    @Override
    protected void buildToStringBuilder(ToStringBuilder tsb) {
        tsb.append("id", id).append("code", code).append("overview", overview);
        super.buildToStringBuilder(tsb);
        tsb.append("commentAccessControl", commentAccessControl);
    }

    // ...
}
</pre>
    </section>
    <section name="BlogEntry">
      <p>
続いてブログの記事に相当するドメインクラス
<a href="apidocs/org/unitedfront2/domain/communication/BlogEntry.html"><code>BlogEntry</code></a>
の考察に入ります。ブログ記事もブログと同様基本インターフェースを実装し、プロパティには
<a href="apidocs/org/unitedfront2/domain/communication/Message.html"><code>Message</code></a>
型の「記事」を持たせています。ブログ記事は
<a href="apidocs/org/unitedfront2/domain/communication/Message.html"><code>Message</code></a>
型を継承して実現することが適切のように思えるかもしれませんが、ここでもブログの「概要」プロパティと同様、委譲さ
せる形をとっています。具象ドメインクラスの継承はオブジェクト指向設計を破壊する原因に繋がりやすいので、極力避
けた方が良いでしょう。
      </p>
      <div class="smaller">
        <table>
          <tr>
            <th>プロパティ名</th>
            <th>型</th>
            <th>説明・解説</th>
          </tr>
          <tr>
            <td><code>entry</code></td>
            <td><a href="apidocs/org/unitedfront2/domain/communication/Message.html"><code>Message</code></a></td>
            <td>記事。記事はブログ記事のタイトルと説明文を持つ。記事の所有者 ID 、参照アクセス権限、編集アクセス権限は、ブログのそれと同一です。記事の著者 ID は所有者 ID と同一です。</td>
          </tr>
        </table>
      </div>
      <p>
ブログ記事には改めて ID プロパティを設けることはしませんでした。ブログ記事にとっての ID は、「記事」プロパティ
が持っている ID （メッセージ ID ）と同値になるからです。ID へアクセスしやすいように、「記事」の ID プロパティ
へのショートカット <code>getId</code> メソッドを用意しました。同様に、「コード」プロパティへのショートカット
<code>getCode</code> も用意しています。
      </p>
<pre name="code" class="java">
/**
 * ブログの記事を表すドメインクラスです。
 *
 */
public class BlogEntry implements Serializable, Identifiable&lt;BlogEntry&gt;,
    Storable, Deletable {

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

    /** 記事 */
    private Message entry;

    // equals, hashCode, toString

    @Override
    public boolean identify(BlogEntry other) {
        if (entry == null) {
            return false;
        }
        return entry.identify(other.getEntry());
    }

    @Override
    public void store() {
        // TODO
    }

    @Override
    public void delete() {
        // TODO
    }

    /**
     * ブログ記事の ID を取得します。
     *
     * @return ブログ記事の ID
     * @ensure ${return} equals ${this.entry.id}
     */
    public Integer getId() {
        return entry.getId();
    }

    /**
     * ブログ記事のコードを取得します。
     *
     * @return ブログ記事のコード
     * @ensure ${return} equals ${this.entry.code}
     */
    public String getCode() {
        return entry.getCode();
    }

    // getters and setters
}
</pre>
    </section>
    <section name="Comment">
      <p>
ブログ記事への投稿コメントは、
<a href="apidocs/org/unitedfront2/domain/communication/Comment.html"><code>Comment</code></a>
クラスをそのまま用いることにします。
      </p>
    </section>
    <section name="BlogValidator">
      <p>
各ドメインモデルの検証クラスを作成します。
<a href="apidocs/org/unitedfront2/domain/communication/Message.html"><code>Message</code></a>
型のプロパティは既存の
<a href="apidocs/org/unitedfront2/domain/communication/MessageValidator.html"><code>MessageValidator</code></a>
を利用すればよいので、ここで新たに作成する必要のあるクラスは
<a href="apidocs/org/unitedfront2/domain/communication/Blog.html"><code>Blog</code></a>
のプロパティ「コード」の検証を行う
<a href="apidocs/org/unitedfront2/domain/communication/BlogValidator.html"><code>BlogValidator</code></a>
のみです。
<a href="apidocs/org/unitedfront2/domain/communication/BlogValidator.html"><code>BlogValidator</code></a>
の完全なソースコードは
<a href="xref/org/unitedfront2/domain/communication/BlogValidator.html"><code>BlogValidator</code> のソースコード</a>
を参照ください。
      </p>
<pre name="code" class="java">
public class BlogValidator implements Serializable {

    /** コードの正規表現のデフォルト ([0-9a-z]+) */
    public static final String DEFAULT_CODE_REGEX = "[0-9a-z]+";

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

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

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

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

    // equals, hashCode, toString

    public void validateCode(String code) throws ValidationException {
        Validate.notBlank(code);
        Validate.maxLength(code, codeMaxLength);
        Validate.match(code, codeRegex);
    }

    public void validateCode(Blog blog) throws ValidationException {
        validateCode(blog.getCode());
    }

    // getters and setters
}
</pre>
    </section>
    <section name="ドメインクラス間の関連">
      <p>最後に、これまでのクラス同士の関連を定義します。</p>
      <p>
ブログとブログ記事、ブログ記事とコメントの関連は <code>transient</code> で定義している点に注意してくだ
さい。これらの関係は常に（永続的に）保障する必要がないため、非永続プロパティとして扱っています。また、それらの
関連を保存するメソッド <code>post</code> と復元するメソッド <code>retrieve*</code> を定義します。
関連の保存は投稿時に行われると考えられるため、ブログおよびブログ記事に <code>post</code> メソッドを用
意しました。また、ブログ記事が存在するブログを安易に削除できないようにするために、
<code>delete</code> メソッドの <code>throws</code> 句に、記事を保持しているブログを削除しようとし
たときに発生する例外
<a href="apidocs/org/unitedfront2/domain/communication/BlogEntryExistException.html"><code>BlogEntryExistException</code></a>
を宣言することにしました。
      </p>
<pre name="code" class="java">
public class Blog ... {

    // ...

    /** ブログ記事リスト */
    private transient List&lt;BlogEntry&gt; entries;

    // ...

    /**
     * 指定した範囲のブログ記事を復元します。記事番号は０から始まる整数で、ID の降順になります。
     *
     * @param start 開始点となる記事番号
     * @param end 終了点となる記事番号
     */
    public void retrieveEntries(int start, int end) {
        // TODO
    }

    /**
     * ブログ記事を投稿します。指定したブログ記事を保存し、このブログと関連付けます。
     *
     * @param blogEntry ブログ記事
     */
    public void post(BlogEntry blogEntry) {
        // TODO
    }

    /**
     * @throws BlogEntryExistException ブログ記事が存在している
     */
    @Override
    public void delete() throws BlogEntryExistException {
        // TODO
    }

    // getters and setters
}
</pre>
<pre name="code" class="java">
public class BlogEntry ... {

    // ...

    /** コメントリスト */
    private transient List&lt;Comment&gt; comments;

    // ...

    /**
     * このブログ記事のブログコメントを復元します。
     */
    public void retrieveComments() {
        // TODO
    }

    /**
     * コメントを投稿します。コメントを保存し、このブログ記事と関連付けます。
     *
     * @param comment コメント
     */
    public void post(Comment comment) {
        // TODO
    }

    // getters and setters
}
</pre>
      <div class="info">
        <h5>Note</h5>
        <p>
<code>Blog</code> と <code>BlogEntry</code> の関係はプロパティによって実現しています。よって、
<code>BlogEntry</code> は、対応するデータベース表が持つであろう <code>blogId</code> プロパティ
を持つ必要はありません。これにより、ドメインモデルとデータベース実装では参照関係が逆転しています。このように、
ドメインモデルのプロパティとデータベース表の列が必ずしも一致するとは限りません。
        </p>
      </div>

<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">
dp.SyntaxHighlighter.ClipboardSwf = 'js/dp.SyntaxHighlighter/Scripts/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code');
</script>

<script type="text/javascript">
var ID='100354378-sbs_dmDesign';
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>
