Eternity's Chit-Chat

aeternum.egloos.com



Domain-Driven Design의 적용-4.ORM과 투명한 영속성 7부 Domain-Driven Design

영속성 관리 REPOSITORY


Hibernate
는 투명한 영속성을 지원하는 오픈 소스 ORM으로 Java 커뮤니티에서 사용되는 ORM의 표준이다. HibernateEJB 3.0의 엔티티 빈 스펙인 JPA에 큰 영향을 끼쳤으며 JBoss의 엔티티빈 구현체로 포함되어 있다. Spring 프레임웍은 Hibernate를 통합하기 편리하도록 여러 가지 지원 클래스들을 제공한다. 주문 시스템 역시 Hibernate를 적용하기 위해 Spring의 지원 클래스를 사용할 것이다.

다음은 주문 시스템의 도메인 모델과 매핑될 데이터 모델이다
.

그림 3 주문 도메인의 데이터 모델


데이터베이스는 HSQLDB를 사용한다. HSQLDB 다음과 같은 명령을 사용하여 실행시키면 된다
.

java -classpath lib\hsqldb.jar org.hsqldb.Server


Hibernate는 XML
형식과 Annotation 형식의 METADATA MAPPING을 지원한다. 본 아티클에서는 XML 형식의 METADATA MAPPING을 사용한다. XML 파일은 각 ENTRY POINT 별로 작성한다. , Customer, Product, Order 별로 하나의 XML 매핑 파일을 작성하고 OrderLineItem에 대한 매핑 정보는 Order 매핑 파일에 포함한다
.

우선 Product PRODUCTS 테이블 간의 매핑을 살펴 보자. 지금까지는 도메인 객체들을 메모리 컬렉션 내에 관리하기 위해 EntryPoint라는 LAYER SUPERTYPE을 상속받아 구현했다. EntryPoint Registrar 클래스가 도메인 객체를 클래스별로 관리할 수 있도록 강제하기 위해 추가된 상위 클래스이다. 따라서 지금까지의 도메인 모델은 Registrar라는 인프라 스트럭처와 결합되어 있었다이처럼 하위 인프라 스트럭처에 의해 사용되는 클래스나 인터페이스에 도메인 레이어의 클래스들이 의존하는 것을 침투적(invasive)이라고 한다.

Spring
Hibernate는 비침투적이다. 비침투적인 프레임워크를 사용할 경우 도메인 클래스들은 하위 인프라스트럭처에 독립적이다. , 프레임워크의 특정 인터페이스를 구현하거나 클래스를 상속받을 필요가 없다. 우리가 궁극적으로 지향하는 순수한 객체 지향 모델로 도메인 모델을 구성할 수 있는 것이다

Order
클래스와 EntryPoint 간의 상속 관계를 제거하자. 이제 Order는 어떤 하위 인프라 스트럭처에도 의존하지 않는 순수한 객체이다. 이처럼 어떤 인프라 스트럭처 코드에도 의존하지 않는 순수한 Java 객체를 POJO(Plain Old Java Object)라고 한다. 주문 ENTITY 추적성을 보장하기 위해서는 Order 클래스에 IDENTITY FIELD를 추가해야 한다. , ORDERS 테이블의의 주 키를 Order 클래스의 속성으로 포함시켜야 한다. ORDERS 테이블의 주 키와 매핑될 Long 타입의 id 속성을 추가하자.

package
org.eternity.customer;

import java.util.HashSet;

import java.util.Set;

 

public class Order {

  private Long id;

  private String orderId;

  private Set<OrderLineItem> lineItems = new HashSet<OrderLineItem>();

  private Customer customer;

 

  public static Order order(String orderId, Customer customer) {

    return new Order(orderId, customer);

  }

 

  Order(String orderId, Customer customer) {

    this.customer = customer;

    this.orderId = orderId;

  }

 

  public Long getId() {

    return id;

  }

EntryPoint로부터의 상속 관계가 제거되었으며 id를 반환하기 위한 getter 메소드가 추가되었다. id를 반환하는 getter 메소드를 추가한 이유는 대부분의 엔터프라이즈 어플리케이션이 프리젠테이션 레이어에서 해당 도메인 객체의 id를 필요로 하기 때문이다.

이제 equals() hashCode()를 구현하자. 앞에서 설명한 바와 같이 equals() hashCode()에서는 대리 키 대신 자연 키를 비교해야 한다. Order를 유일하게 식별하면서도 잘 변하지 않는 값이 무엇일까? orderId를 사용하면 될 것 같다


  String getOrderId() {
    return orderId;
  }

  publicboolean equals(Object object) {
    if (object == this) {
      return true;
    }

    if
(!(object instanceof Order)) {
      return false;
    }

    final
Order other = (Order)object;
    return this.orderId.equals(other.getOrderId());
  }

  public
int hashCode() {
    return this.orderId.hashCode();
  }

equals()
에서 파라미터로 전달된 객체의 orderId 파라미터를 비교하지 않고 별도의 getOrderId() 메소드를 호출한 이유는 비교 대상으로 Proxy 객체가 전달될 수도 있기 때문이다. 따라서 equals()에서 비교를 수행하기 위해서는 getter 메소드를 직접 호출해서 지연 로딩이 일어나도록 해야한다. 도메인 모델이 하부 인프라스트럭처에 완전히 독립적일 수 없다는 것이 바로 이런 이유때문이다. ORM으로 Hibernate를 쓰는 경우 Hibernate에 의한 제약사항이 도메인 모델의 구현에 미세하나마 영향을 미친다. 그러나 자기 캡슐화(self-encapsulation)는 좋은 습관이므로 지킨다고 해서 손해볼 것은 없을 것이다.


핑백

  • Domain-Driven Design | Jongmin Kim&#039;s Blog 2014-09-02 01:18:12 #

    ... /07/13 Domain-Driven Design의 적용-4.ORM과 투명한 영속성 8부 [2] 2009/06/29 Domain-Driven Design의 적용-4.ORM과 투명한 영속성 7부  2009/06/23 Domain-Driven Design의 적용-4.ORM과 투명한 영속성 6부 [2] 20 ... more