#04장 Repo와 모델 구현 (JPA 중심)

최범균 저「DDD START!, 2016」를 읽고 정리하였습니다.

  • 다룰 내용

    • JPA 이용 리포 구현

    • 엔티티와 밸류 매핑

    • 밸류 컬렉션 매핑

    • 애그리거트 로딩 전략과 영속성 전파

    • 식별자 생성 기능

JPA 이용 리포 구현

  • JPA: 도메인 모델과 리포 구현시 선호되는 기술 (ORM 기반)

  • 가능한, 리포 구현 클래스를 infra 영역에 위치 시켜 의존성을 낮춰야

  • 기본 구현 방법

    • ID로 애그 조회

      public Order findById(OrderNo no){
      	em.find(Order.class, no);
      }
    • 애그 저장

      public void save(Order order){
      	em.persist(order);
      }
    • 삭제

      // 고객이 요구해도, 보통 바로 삭제는 안함 (관리자가 삭제한 데이터도 조회해야될때도 있으니)
      public void remove(Order order) {
      	entityManager.remove(order);
      }
  • 트랜잭션 예시

  • 명명 규칙: findBy 뒤에 조건 (e.g., findByOrdererld())

  • ID 외로 agg 조회시 Criteria나 JPQL 사용

매핑

  • 애그와 JPA 매핑을 위한 기본 규칙

    • agg 루트는 엔티티이므로 @Entity로 매핑 설정

    • 한 테이블에 엔티티와 밸류가 공존시

      • 밸류 는 @Embeddable로 설정

      • 밸류 는 @Embedded로 설정

  • 다이어그램을 보고, 코드로 확인해보자.

    • 사례: 주문 (엔티티), 주문자(밸류), 배송지정보(밸류) → 주문 내역서 (purchase_order) 테이블에 다 포함

  • @Entity

  • @Embeddable, @Embedded

  • JPA 2 부터 @Embeddable은 중첩 허용해서, 아래 처럼, Shippinglnfo 안에 Address랑 Receiver 포함

기본 생성자

필드 접근 방식 사용

  • 엔티티를 객체가 제공할 기능 중심으로 구현하도록 유도하면, JPA 매핑 처리를 프로퍼티 방식이 아닌 필드방식으로 선택해서, 불필요한 get/set메서드를 구현하지 말아야 한다.

AttributeConverter를 이용한 밸류 매핑 처리

  • 밸류 타입의 프로퍼티를 한개 컬럼에 매핑해야될때가 있다. (e.g. DB에 1000mm 저장)

  • AttributeConverter: 밸류 타입과 칼럼 데이터 간의 변환 처리를 해준다.

밸류 컬렉션: 별도 테이블 매핑

  • 밸류 컬렉션 별도 테이블로 매핑할때는 @ElementCollection과 @CollectionTable 사용

밸류 컬렉션: 한개 컬럼 매핑

  • 사례: 도메인 모델에서 이메일을 Set으로 보관하고, DB에서는 한컬럼에 콤마로 구분해서 저장할때

  • AttributeConverter 사용 하기

밸류를 이용한 아이디 매핑

  • 식별자 대게 String이나 Long

  • 근데, 밸류타입을 식별자로 지정하려면, @Id대신 @EmbeddedId 사용

  • 위와 같이 사용해서 장점은, 주문번호가 1세대 2세대가 있다면, OrderNo 안에서 구분짓게 할 수 있다.

별도 테이블에 저장하는 밸류 매핑

  • 상품 화면에 리뷰가 있다고 리뷰가 상품 애그리거트에 포함되있는게 아니다. 서로의 변경이 영향을 주지 않기에, 리뷰는 엔티티가 맞지만 상품 애그리거트에 속한 것은 아니다.

  • 밸류인지 애그리거트인지 구별 방법: 고유식별자 갖는지 확인하면 된다. 다만, DB에 넣기 위한 ID 식별자와 헷갈리지 말자.

  • 예, Aritcle(엔티티) - ArticleContent(밸류타입)

  • 밸류를 매핑한 테이블 지정은 @SecondaryTable와 @AttributeOverrides 사용

밸류 컬렉션을 @Entity로 매핑하기

  • 개념적으로 밸류인데 기술 제약으로 엔티티로 사용해야 될때가 있다.

  • 예를 들어, 상속 구조를 갖는 밸류 타입을 사용하면, @Embeddable대신 @Entity 사용

    • 아래 Image는 엔티티로 선언된다. 다만, 사실상 밸류임으로 상태 변경하는 기능은 추가하지 않는다.

    • Image의 라이프사이클은 Product에 달려있다. (orphanRemoval true로 사용)

ID 참조와 조인 테이블을 이용한 단방향 M:N 매핑

  • 애그리거트 간 집합 연관은 피해야하지만, 그렇게 하는 것이 유리하다면, ID 참조를 이용한 단방향 집합 연관을 적용할 수 있다.

애그리거트 로딩 전략

  • JPA 매핑을 설정시, 애그리거트에 속한 객체가 모두 모여야 완전한 하나가 된다는 것을 알아두자.

    • 속한 모든 객체가 완전한 상태여야 함을 의미한다.

  • 조회 시점에서 애그리거트를 완전한 상태가 되도록 하려면 → FetchType.EAGER

    • 하지만 항상 좋은것은 아니다. 책에선 카타시안 조인 나타나는 문제를 보여준다.

애그리거트의 영속성 전파

  • 완전한 하나라는 것은, 조회 뿐만아니라 저장하고 삭제할 때도 하나로 처리

  • 저장/삭제시, 루트뿐만 아니라 속한 모든 객체 저장/삭제하기

    • Cascade 사용

식별자 생성 기능

  • 방법

    • 사용자가 직접 생성 (e.g., 이메일)

    • 도메인 로직으로 생성

    • DB 일련번호 사용

엔티티가 식별자 생성 기능을 제공하는 것보다는 별도 서비스로 식별자 생성 기능을 분리해야 한다.

식별자 생성의 적합한 또 다른 장소는 리포지터리이다.

Last updated