모놀리식 DDD 기반 Review 도메인 설계와 구현

한소연·2026년 3월 4일

내일배움캠프

목록 보기
4/21
post-thumbnail

이번 글에서는 모놀리식 DDD 구조에서 Review 도메인을 설계하고 구현한 과정을 정리했습니다.
특히, 도메인 구조와 역할, VO, Repository, Entity 설계 흐름까지 자세히 다뤄볼게요.


🔷 Domain이란?

Domain(도메인)은 애플리케이션에서 핵심 비즈니스 로직과 규칙을 구현하는 계층입니다.

  • Controller: HTTP 요청/응답, DTO 변환, 인증 정보 추출

  • Application: 유스케이스 처리, 트랜잭션 관리, 도메인 호출

  • Domain: 핵심 비즈니스 규칙, 엔티티/VO, 정책/검증

  • Infrastructure: DB, 외부 API, 구현체

Domain은 “무엇을 어떻게 처리할 것인가”라는 비즈니스 결정과 로직을 담는 곳입니다.


🔷 Domain 구조

모놀리식 DDD 기준, Review 도메인은 다음과 같이 구성했습니다.

review
 └─ domain
      ├─ Review.java          // 엔티티
      ├─ ReviewId.java        // VO (Value Object, PK)
      └─ exception
           └─ ReviewException.java
  • Review : 리뷰 엔티티, 도메인 로직 포함

  • ReviewId : UUID 기반 PK, Value Object

  • ReviewException : 비즈니스 규칙 위반 시 예외 처리


🔷 Review Entity 설계

@Entity
@Table(name = "p_review")
@Getter
public class Review {

    @EmbeddedId
    private ReviewId reviewId;

    private Long reviewerId;
    private String reviewerName;
    private Integer rating;
    private String comment;

    @OneToOne
    @JoinColumn(name = "order_id")
    private Order order;

    protected Review() {}

    private Review(Long reviewerId, String reviewerName, Integer rating, String comment, Order order) {
        this.reviewId = ReviewId.of();
        this.reviewerId = reviewerId;
        this.reviewerName = reviewerName;
        this.rating = rating;
        this.comment = comment;
        this.order = order;

        validate(order);
    }

    public static Review create(Long reviewerId, String reviewerName, Integer rating, String comment, Order order) {
        return new Review(reviewerId, reviewerName, rating, comment, order);
    }

    private void validate(Order order) {
        if (!reviewerId.equals(order.getOrdererId())) {
            throw new ReviewNotAllowedException("주문자만 작성 가능");
        }
        if (order.getStatus() != OrderStatus.DELIVERED) {
            throw new ReviewNotAllowedException("주문 완료 후 작성 가능");
        }
    }
}

✅핵심 포인트

  1. ReviewId를 @EmbeddedId로 사용 → UUID 기반 PK

  2. validate() 메서드에서 주문자 일치 여부와 주문 완료 여부 확인 → 도메인 로직

  3. create() 팩토리 메서드로 객체 생성 → 생성 시 바로 검증


🔷 VO와 Repository

1. ReviewId -> VO

@Embeddable
public class ReviewId implements Serializable {
    @Column(name="review_id", nullable=false, updatable=false)
    private UUID value;

    protected ReviewId() {}
    public ReviewId(UUID value) { this.value = value; }
    public static ReviewId of() { return new ReviewId(UUID.randomUUID()); }
}
  • VO(Value Object) → PK 관리

  • DB 컬럼명 지정 위해 @Column 사용

  • UUID 생성 로직 포함 → 도메인에서 PK 자동 생성

2. ReviewRepository

public interface ReviewRepository extends JpaRepository<Review, ReviewId> {

    Optional<Review> findByOrder_OrderId(UUID orderId);
    List<Review> findAllByReviewerId(Long reviewerId);

}
  • JPA Repository 상속 → CRUD, 조회 기능 사용

  • 주문 기준, 작성자 기준 조회 가능


🔷 Review 도메인 흐름

  1. 주문 완료 확인 → Order.getStatus() == DELIVERED

  2. 작성자 확인 → reviewerId == order.getOrdererId()

  3. 리뷰 생성 → Review.create(...) 호출

  4. DB 저장 → reviewRepository.save(review)

💡 요약하면:

  • 비즈니스 규칙 → 도메인에서 검증
  • DB 접근 → Repository에서 수행
  • 도메인과 Repository를 분리 → DDD 원칙 준수

🔷 정리

  • 도메인만으로 리뷰 작성 로직을 안전하게 구현 가능

  • VO를 통한 PK 관리 → UUID 기반, 재사용 가능

  • Repository는 단순 DB 접근 → CRUD 및 조회 지원

  • 비즈니스 규칙 검증과 DB 접근을 명확히 분리 → 유지보수, 테스트 용이

이번 글에서는 Review 도메인을 중심으로 DDD 구조, Entity, VO, Repository, 검증 흐름까지 자세히 살펴봤습니다.
아직 정리해야 할 곳이 더 많지만 추후 Service와 Controller 부분을 작업하며 더 다듬어 갈 예정입니다.

profile
안 되면 될 때까지

0개의 댓글