[Spring Boot] 도메인 모델 패턴 적용 - JPA + Aggregate + VO

이홍준·2023년 7월 12일
0

Spring Boot

목록 보기
6/11

DDD를 공부하면서 도메인 모델 패턴에 대해서 알게 되었다. 그래서 JPA로 기본적인 Aggregate를 구현하고자 한다.

Aggregate 구성도

Order라는 Aggregate 안에 각 VO로된 필드들이있다. 배송정보를 포함하는 DeliveryInfo, 주문한 상품목록인 OrderItem, 상품당 가격을 나태난 Money등이 있다.

Money

package com.tddnote.order;

import lombok.*;

import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
@Access(AccessType.FIELD)
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode 
public class Money {
    private String currency;
    @Column(name = "money_value") // 예약어 오류 방지
    private int value;
}
  • 멤버변수를 객체 타입을 사용해야 하기 때문에 클래스에 @Embeddable를 추가해주고 VO의 동등성을 보장하기위해 @EqualsAndHashCode를 추가해준다.
  • VO는 식별자가 없기 때문에 AccessType설정을 따로 해줘야 한다.

OrderItem


import lombok.*;

import javax.persistence.*;

@Embeddable
@Access(AccessType.FIELD)
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class OrderItem {

    private String product;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name="value", column = @Column(name="price"))
    })
    private Money price;
}
  • Money 라는 VO를 가지고있는 OrderItem 클래스다.
  • 마찬가지로 VO이기 때문에 관련 어노테이션을 추가해준다.
  • @AttributeOverides를 통해 @Embedded 된 멤버의 컬럼명을 받을 수 있다. (안 받으면 value컬럼명이 예약어라서 error가 발생한다)

DeliveryInfo


import lombok.*;
import javax.persistence.*;

@Embeddable
@Access(AccessType.FIELD)
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class DeliveryInfo {

    private String address;
    private String phone;

}
  • 배달 정보를 담은 VO 다. 마찬가지로 기본 설정을 해준다.

Order


import lombok.*;

import javax.persistence.*;
import java.util.List;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ElementCollection
    @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
    private List<OrderItem> items;

    @Embedded
    private DeliveryInfo deliveryInfo;
}
  • 해당 VO들을 담은 Aggregate이자 Entity클래스인 Order이다.
  • @ElementCollection, @CollectionTable를 선언해서 OrderItem과같이 복수로 가져가는 VO일 경우 따로 테이블을 만든다.

적용하기

  • 주문을 등록하고자 하는 요청할 RequestBody이다.
    {
      "deliveryInfo": {
        "address": "서울시 강남구 언주로 123",
        "phone": "010-1234-5678"
      },
      "items": [
        {
          "price": {
            "currency": "won",
            "value": 1000000
          },
          "product": "아이폰 12 Pro"
        },
    		{
          "price": {
            "currency": "won",
            "value": 1200000
          },
          "product": "아이폰 12 Pro Max"
        }
      ]
    }
  • ORDERS 테이블

    ORDERS

    • VO객체인 DeliveryInfo의 속성들이 order 테이블에 마치 하나의 객체처럼 들어있다.
  • ORDER_ITEMS

    ORDER_ITEMS

    • 마찬가지로 VO객체인 Money의 속성들이 그대로 들어있고 ORDER_ID라는 참조키를 가지고 있다.

결론

JPA를 통해 VO등이 들어간 Aggregate를 간단하게 구현해보았다. VO를 멤버변수로 사용하는 이유는 명료성과 자가 유효성 검사를 통해 Entity의 역할을 좀 더 분리 할 수 있어 확장성에 더 용이하다고 생각한다. mybatis처럼 쿼리로 집어넣는 경우였다면 직접 타입을 적어서 넣어줬어야 했었다. 하지만 JPA와 같은 ORM방식의 장점이 돋보였던 점이 여기서 나타난다. 객체 변수를 따로 조작할 필요가 없이 직관적으로 그대로 적용된다는 것이 돋보였다.

profile
I'm a web developer.

0개의 댓글