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; } }
links
第1回 Spring+HibernateEntityManager(HibernateEntityManager単体編)
第2回 Spring+HibernateEntityManager(とりあえずSpring編)
第3回 Spring+HibernateEntityManager(宣言的トランザクション編)
第4回 Spring+HibernateEntityManager(Spring+DDDっぽく編)
第5回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その2)
第6回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その3)
第7回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その4)
第8回 Spring+HibernateEntityManager(@Transactionalアノテーション編)
第9回 Spring+HibernateEntityManager(@Required編)
第10回 Spring+HibernateEntityManager(XMLからの外部リソース参照編)
第11回 Spring+HibernateEntityManager(AspectJ AOP with Load-Time-Weaver編)
第12回 Spring+HibernateEntityManager(DBCPのvalidationQuery編)
第13回 Spring+HibernateEntityManager(@Resource編)
第14回 Spring+HibernateEntityManager(コンポーネント自動検出 with アノテーション編)
第15回 Spring+HibernateEntityManager(コンポーネント自動検出 without アノテーション編)