DDD에 대해 이해하고 프로젝트에 적용해보자

DDD의 개념과 등장 배경

  • 소프트웨어를 설계할 때 가장 중요한 것 = 고객의 요구사항을 정확히 이해하는 것
  • 과거 : 기술 중심 개발 방법론 비즈니스 측면에서 발생하는 요구사항 반영에 한계

DDD의 핵심 가치

  • 소프트웨어 설계에서 도메인 지식이 가장 중요한 요소임을 인식하고 이를 중심으로 소프트웨어 설계
  • 유비쿼터스 언어 : 모든 곳에서 동일하게 사용하는 언어
  • 용어의 모호함을 줄이고 도메인과 코드 사이 불필요한 해석 과정 감소

도메인 모델

  • DDD에서 핵심 개념을 포현하는 방법
  • 특정 도메인에 대한 지식, 규칙, 로직을 추상화하여 개념적으로 표헌한 것

DDD의 구조와 용어

Entity

  • 고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 가짐
  • 데이터와 함께 기능을 제공하는 객체
  • 도메인 관점에서 기능을 구현하고 기능 구현을 캡슐화해서 데이터가 임의로 변경되는 것 방지

Value

  • 고유의 식별자를 갖지 않는 객체로 개념적인 하나의 값을 표현

Domain Service

  • 특정 엔티티에 속하지 않는 도메인 로직

Aggregate

  • 관련된 객체들을 모아 하나의 단위로 취급하는 개념
  • 연관 도메인을 애그리거트로 묶어 하나의 군집으로 이해
  • 상위 수준에서 도메인 모델 간의 관계 파악 가능
  • 특정 도메인 군집에 속한 객체들을 관리하는 루트 엔티티를 가짐
  • 하나의 애거리거트에는 반드시 하나의 루트 엔티티가 존재

JPA에서의 DDD

  • 비즈니스 로직과 도메인 모델 간의 결합도를 낮추기 위해 repository 패턴 권장
  • JpaRepository, @Entity
  • 도메인 모델이 애플리케이션의 핵심
  • 데이터베이스 테이블에 대한 매핑을 하면서도 도메인 모델의 순수성 유지 가능

그럼 mybatis로 DDD 개발은 어렵나…? 똑같이 repository로 하는거라 상관없나..? 흠

  • JpaRepository를 루트 애그리거트에만 구현
    • 루트 애그리거트가 하위 애그리거트 관리
    • 영속성 로직도 루트 애그리거트에 집중
    • ex) Order(root) OrderProduct Receipt 가격 정보 수정
@Transactional
public void updateOrderProductReceiptPrice(Long id, Long orderProductId, Long receiptId, Long price) {
	orderRepository.findById(id)
			.flatMap(order -> order.getProduct(orderProductId))
			.flatMap(orderProduct -> orderProduct.findReceipt(receiptId))
			.ifPresent(receipt -> receipt.update(price));
}

OrderProduct는 Order에 의해 변경 되고, Receipt는 OrderProduct에 의해 변경됨 캡슐화가 깨지지 않음!

MSA 에서의 DDD

  • 모놀리식 애플리케이션을 MSA로 전환할 때 중요한 것 : 서비스의 경계를 명확히 나누는 것
  • 도메인 개념을 기준으로 마이크로서비스 구축(주문, 상품, 리뷰 등)