본문 바로가기
programming/DDD

[DDD] 4장 리포지터리와 모델 구현

by yhsim98 2022. 12. 21.

JPA를 이용한 리포지터리 구현

  • 역시 RDBMS를 사용할 때, ORM 만한 매핑 기술이 없다
  • 자바 ORM 표준인 JPA를 이용해서 리포지터리와 애그리거트를 구현하는 방법에 대해 살펴보자

모듈 위치

  • 라포지터리 인터페이스는 도메인 영역, 리포지터리를 구현한 클래스는 인프라스트럭처 영역에 속한다

4.3.3 필드 접근 방식 이용

  • 엔티티에 프로퍼티를 위해 공개 get/set 메서드를 추가하면 도메인의 의도가 사라지고 객체가 아닌 데이터 기반으로 엔티티를 구현할 가능성이 높아진다
  • 그래서 JPA 매핑 처리를 프로퍼티가 아닌 필드 방식으로 선택하여 불필요한 get/set 메서드를 구현하지 말자
@Entity
@Access(AccessType.FIELD)
public class Order {
    @EmbeddedId
    private OrderId id;

    ...
    // cancel(), changeShippingInfo() 등 도메인 기능 구현
}

4.3.4 AttributeConverter를 이용한 밸류 매핑 처리

  • 두 개 이상의 프로퍼티를 가진 밸류 타입을 한 개 칼럼에 매핑하려면 @Embeddable 어노테이션으로는 처리할 수 없다
  • 이럴 때 AttributeConverter 사용함
  • 밸류 타입과 칼럼 데이터 간의 변환을 처리하기 위한 기능을 정의한다
// javax.persistence 에서 제공하는 기능
public interface AttributeConverter<X, Y> {
    public Y convertToDatabaseColumn (X attribute);
    public X convertToEntityAttribute (Y dbData);
}
@Converter(autoApply = true)
public class MoneyConverter implements AttributeConverter<Money, Integer> {

    @Override
    public Integer convertToDatabaseColumn(Money money) {
        return money == null ? null : money.getValue();
    }

    @Override
    public Money convertToEntityAttribute(Integer value) {
        return value == null ? null : new Money(value);
    }
}
  • AttributeConverter 인터페이스를 구현한 클래스는 @Converter 어노테이션을 적용한다
  • autoApply = true로 한다면 모델에 출현하는 모든 Money 타입의 프로퍼티에 대해 MoneyConverter를 자동으로 적용해 준다
  • false면 프로퍼티 값을 변환할 때 사용할 컨버터를 직접 지정해야 한다
@Column(name = "aaa")
@Convert(converter = MoneyConverter.class)
private Money totalAmounts;

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

  • Order 엔티티는 한 개 이상의 OrderLine을 가질 수 있다
  • OrderLine은 밸류 컬렉션이고, 밸류 컬렉션을 별도 테이블로 매핑할 때는 @ElementCollection@CollectionTable을 함께 사용한다
@Entity
@Table(name = "purchase_order")
public class Order {
    ...
    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "order_line",
        joinColumns = @JoinColumn(name = "order_number"))
    @OrderColumn(name = "line_idx")
    private List<OrderLine> orderLines;
    ...
}

@Embeddable
public class OrderLine {
    @Embedded
    private ProductId productId;

    @Column(name = "price")
    private Money price;

    @Column(name = "quantity")
    private int quantity;
    ...
}
  • OrderLine 클래스에는 List의 인덱스 값을 저장하기 위한 프로퍼티가 존재하지 않는다
  • List 자체가 인덱스를 가지기 때문
  • @OrderColumn를 이용하여 지정한 칼럼에 리스트의 인덱스 값을 저장한다

4.3.7 밸류를 이용한 ID 매핑

  • @Id가 아닌 @EmbeddedId를 이용하여 매핑해야 함
  • Id로 사용할 밸류 타입은 Serializable를 상속받아야 한다

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

  • 애그리거트에서 루트 엔티티를 뺀 나머지 구성요소는 대부분 밸류이다
  • 루트 엔티티 외에 또 다른 엔티티가 있다면 진짜 엔티티인지 의심해봐야 한다
    • 단지 별도 테이블에 데이터를 저장한다고 해서 엔티티인 것은 아니다
    • 주문 애그리거트도 OrderLine을 별도 테이블에 저장하지만 OrderLine 자체는 엔티티가 아니라 밸류이다
  • 밸류가 아니라 엔티티가 확실하다면 해당 엔티티가 다른 애그리거트는 아닌지 확인해야 한다
  • 특히 자신만의 독자적인 라이프 사이클을 갖는다면 애그리거트일 가능성이 높다
    • 예를 들어 상품과 리뷰는 같은 애그리거트라고 생각하기 좋지만
    • 같이 생성되지도 않고 함께 변경되지도 않기 때문에 같은 애그리거트에 속하지는 않는다
  • Article과 Article_Content를 생각하면, ArticleContent를 별도의 엔티티로 생각할 수 있지만
  • ArticleContetn는 Article의 내용을 담고 있는 밸류로 생각하는 것이 맞다
    • ArticleContent의 ID는 식별자이긴 하지만, 이 식별자를 사용하는 이유는 Article 테이블의 데이터와 연결하기 위함이지 Article_Content를 위한 별도 식별자가 필요하기 때문은 아니다
    • 즉 이것은 게시글의 특정 프로퍼티를 별도 테이블에 보관한 것으로 접근해야 한다

'programming > DDD' 카테고리의 다른 글

[DDD] 3장 애그리거트  (0) 2022.12.10
[DDD] 2장 아키텍처 개요  (0) 2022.11.21
DDD-Light  (0) 2022.10.21