이번 글에서는 모놀리식 DDD 구조에서 Review 도메인을 설계하고 구현한 과정을 정리했습니다.
특히, 도메인 구조와 역할, VO, Repository, Entity 설계 흐름까지 자세히 다뤄볼게요.
Domain(도메인)은 애플리케이션에서 핵심 비즈니스 로직과 규칙을 구현하는 계층입니다.
Controller: HTTP 요청/응답, DTO 변환, 인증 정보 추출
Application: 유스케이스 처리, 트랜잭션 관리, 도메인 호출
Domain: 핵심 비즈니스 규칙, 엔티티/VO, 정책/검증
Infrastructure: DB, 외부 API, 구현체
Domain은 “무엇을 어떻게 처리할 것인가”라는 비즈니스 결정과 로직을 담는 곳입니다.
모놀리식 DDD 기준, Review 도메인은 다음과 같이 구성했습니다.
review
└─ domain
├─ Review.java // 엔티티
├─ ReviewId.java // VO (Value Object, PK)
└─ exception
└─ ReviewException.java
Review : 리뷰 엔티티, 도메인 로직 포함
ReviewId : UUID 기반 PK, Value Object
ReviewException : 비즈니스 규칙 위반 시 예외 처리
@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("주문 완료 후 작성 가능");
}
}
}
ReviewId를 @EmbeddedId로 사용 → UUID 기반 PK
validate() 메서드에서 주문자 일치 여부와 주문 완료 여부 확인 → 도메인 로직
create() 팩토리 메서드로 객체 생성 → 생성 시 바로 검증
@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 자동 생성
public interface ReviewRepository extends JpaRepository<Review, ReviewId> {
Optional<Review> findByOrder_OrderId(UUID orderId);
List<Review> findAllByReviewerId(Long reviewerId);
}
JPA Repository 상속 → CRUD, 조회 기능 사용
주문 기준, 작성자 기준 조회 가능
주문 완료 확인 → Order.getStatus() == DELIVERED
작성자 확인 → reviewerId == order.getOrdererId()
리뷰 생성 → Review.create(...) 호출
DB 저장 → reviewRepository.save(review)
💡 요약하면:
- 비즈니스 규칙 → 도메인에서 검증
- DB 접근 → Repository에서 수행
- 도메인과 Repository를 분리 → DDD 원칙 준수
도메인만으로 리뷰 작성 로직을 안전하게 구현 가능
VO를 통한 PK 관리 → UUID 기반, 재사용 가능
Repository는 단순 DB 접근 → CRUD 및 조회 지원
비즈니스 규칙 검증과 DB 접근을 명확히 분리 → 유지보수, 테스트 용이
이번 글에서는 Review 도메인을 중심으로 DDD 구조, Entity, VO, Repository, 검증 흐름까지 자세히 살펴봤습니다.
아직 정리해야 할 곳이 더 많지만 추후 Service와 Controller 부분을 작업하며 더 다듬어 갈 예정입니다.