Spring+HibernateEntityManager(Spring+DDDっぽく編 その3)

インフラストラクチャ層の位置づけ

インフラストラクチャ層は、上位層から利用される層という位置づけで、内容的には何でもありです。プレゼンテーション層、アプリケーション層、ドメイン層のすべてから依存が可能です。しかし、インフラストラクチャ層は上位層に依存してはいけません。(とおいらは思う)

インフラストラクチャ層(API

インフラストラクチャ層で API というと矛盾を感じる人もいるかもしれません。インフラストラクチャという用語を一般的な文脈で捉えると、たしかにそのような矛盾を感じてもおかしくありません。しかし、ここでは DDD という文脈で捉える必要があります。「インフラストラクチャ層≠実装の層」であり、「インフラストラクチャ層≠EIS層」です。インフラストラクチャ層は、あくまでも概念階層的に下層に位置しているにすぎません。(とおいらは思う)

HasGlobalIdentity インタフェース(infrastracture-api/src/main/java/infrastracture/HasGlobalIdentity.java)
package infrastracture;

/**
 * グローバル・アイデンティティ(通常はデータベース・アイデンティティと同義語)を持っている事を表すインタフェースです。
 * <p>
 * グローバル・アイデンティティやデータベースアイデンティティというと、実装依存なものとして捉えられるかもしれませんが、
 * ドメイン層の概念レベルから利用されるものです。
 * </p>
 * <p>
 * 例として、人間をエンティティとして捉えてみます。同姓同名なら同じ人間として認識されるかというと、
 * 多くの場合はそうではありません。さらに年齢や性別や住所が同じでも、多くの場合は同一の人間とみなされません。
 * さらに突き詰めていくと、同一性とは何かという問題になりますが、その同一性の定義はドメイン層で行われます。
 * そして、その同一性という概念をサポートするのが、この HasGlobalIdentity インタフェースです。
 * </p>
 * <p>
 * しかし、同一性という概念が存在するのはよいとしても、「なぜグローバル・アイデンティティを get できる必要があるのか」
 * という疑問が湧きます。確かに、比較さえできれば問題なさそうに思えます。それは、アイデンティティ自体がエンティティの
 * プロパティと切り離して考えられる概念であるためであり、切り離される概念であれば、それ単体で取り扱うことも可能と考えられる
 * からです。(とか考えてますが、まだまだ思索中なので、批判的なアイデア歓迎ですw)
 * </p>
 * 
 * @param <I>
 *            グローバル・アイデンティティの型
 * @author beyondseeker
 * @version $Id$
 */
public interface HasGlobalIdentity<I> {
    
    /**
     * グローバル・アイデンティティを返します。
     * 
     * @return グローバル・アイデンティティ
     */
    I getId();
    
}
DomainEntity インタフェース(infrastracture-api/src/main/java/infrastracture/DomainEntity.java)
package infrastracture;

/**
 * ドメイン(業務問題領域)のエンティティの基底インタフェースです。
 * ドメインのエンティティは、必ずこのインタフェースを継承する必要があります。
 * 
 * @param <I>
 *            グローバル・アイデンティティの型
 * @author beyondseeker
 * @version $Id$
 */
public interface DomainEntity<I> extends HasGlobalIdentity<I> {
    
    /**
     * 自身と指定された DomainEntity が同一かどうかを返します。
     * 
     * @param another
     *            比較対象の DomainEntity
     * @return 自身と指定された DomainEntity が同一の場合のみ true
     */
    boolean isSameAsDomainEntity(DomainEntity<?> another);
    
    /**
     * DomainEntity としてのインタフェースを返します。実行時のクラスにに依存せず、静的にインタフェースを決定する目的で使用されます。
     * {@link #getClass()} によってクラスを取得する場合、プロキシー等のラッパークラスが返される可能性があります。
     * 
     * @return DomainEntity としてのインタフェース
     */
    Class<?> getDomainEntityInterface();
    
}

インフラストラクチャ層(実装)

DomainEntityBase クラス(infrastracture-impl/src/main/java/infrastracture/DomainEntityBase.java)
package infrastracture;

import javax.persistence.MappedSuperclass;

/**
 * {@link DomainEntity} のベース実装クラスです。
 * 
 * @param <I>
 * @author beyondseeker
 * @version $Id$
 */
@MappedSuperclass
abstract public class DomainEntityBase<I> implements DomainEntity<I> {
    
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSameAsDomainEntity(DomainEntity<?> another) {
        
        // 自分自身の ID が存在しない場合、同一とはみなされない。
        if (this.getId() == null) return false;
        
        // 自分と相手の DomainEntity としてのインタフェースが異なる場合は、同一とみなされない。
        // 実行時の型の比較でないことに注意。
        if (!this.getDomainEntityInterface().equals(another.getDomainEntityInterface())) return false;
        
        // 自分と相手の グローバルアイデンティティが ID が同一の場合は、同一の DomainEntity とみなされる。
        return this.getId().equals(another.getId());
        
    }
    
}
DomainEntityBaseWithSurrogateIntegerKey クラス(infrastracture-impl/src/main/java/infrastracture/DomainEntityBaseWithSurrogateIntegerKey.java)
package infrastracture;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

/**
 * {@link Integer} 型の代理キーを使用する {@link DomainEntityBase} クラスです。
 * 
 * @author beyondseeker
 * @version $Id$
 */
@MappedSuperclass
abstract public class DomainEntityBaseWithSurrogateIntegerKey extends DomainEntityBase<Integer> {
    
    /**
     * Global Identity
     */
    @Id
    @GeneratedValue
    private Integer id;
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Integer getId() {
        return id;
    }
    
    /**
     * グローバル・アイデンティティをセットします。
     * 
     * @param id
     *            Global Identity
     */
    public void setId(Integer id) {
        this.id = id;
    }
    
}
ServiceBase クラス(infrastracture-impl/src/main/java/infrastracture/ServiceBase.java)
package infrastracture;

import javax.persistence.EntityManager;

/**
 * Service のベースクラスです。現段階では、このサンプルは DAO 等の仕組みを持たず、Service 実装内で直接 EntityManager
 * を使用するため、 EntityManager を DI されるようになっています。
 * 
 * @author beyondseeker
 * @version $Id$
 */
public class ServiceBase {
    
    /**
     * EntityManager
     */
    private EntityManager entityManager;
    
    /**
     * EntityManager をセットします。
     * 
     * @param entityManager
     *            EntityManager
     */
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
    
    /**
     * EntityManager を返します。
     * 
     * @return EntityManager
     */
    public EntityManager getEntityManager() {
        return entityManager;
    }
    
}