Study/도메인 주도 개발 시작하기

chapter 02. 아키텍처 개요(3) 도메인 영역의 주요 구성요소

hongeeii 2024. 2. 13. 22:49
728x90
반응형

도메인 영역의 주요 구성요소

엔티티(Entity)

고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 갖는다.
주문(Order), 회원(Member), 상품(Product)와 같이 도메인의 고유한 개념을 표현한다.
도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공함.

밸류(Value)

고유의 식별자를 갖지 않는 객체로 주로 개념적으로 하나인 값을 표현할 때 사용
엔티티의 속성으로 사용할 뿐 아니라 다른 밸류 타입의 속성으로도 사용할 수 있다.

애그리거트(Aggregate)

애그리거트는 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것.
예를 들어 주문과 관련된 Order 엔티티, OrderLine 밸류, Orderer 밸류 객체를 주문 애그리거트로 묶을 수있다.

Repository

도메인 모델의 영속성을 처리

도메인 서비스(Domain Service)

특정 엔티티에 속하지 않은 도메인 로직을 제공
도메인 로직이 여러 엔티티와 밸류를 필요로 하면 도메인 서비스에서 로직을 구현한다.
예를 들면 '할인 금액 계산'은 상품, 쿠폰, 회원등급, 구매 금액 등 다양한 조건을 이용해서 구현하게 됨.

엔티티와 밸류

도메인 모델의 엔티티와 DB 모델의 엔티티는 같은 것이 아니다.

  • 가장 큰 차이점은 도메인 모델의 엔티티는 데이터와 함께 도메인 기능을 함께 제공한다.
class Order(
    private val shippingInfo: ShippingInfo,
    private val orderNo: OrderNo,
    private val orderer: Orderer
    ) {

    // 도메인 모델은 도메인 기능도 함께 제공
    fun changeShippingInfo(newShippingInfo: ShippingInfo){
        
    }
}

도메인 모델의 엔티티는 단순히 데이터를 담고 있는 데이터 구조라기보다는 데이터와 함께 기능을 제공하는 객체.
도메인 관점에서 기능을 구현하고 기능 구현을 캡슐화해서 데이터가 임의로 변경되는 것을 막는다.

  • 또 다른 차이점은 도메인 모델의 엔티티는 두 개 이상의 데이터가 개념적으로 하나인 경우 밸류 타입을 이용해서 표현할 수 있다.
    class Orderer (
    private val name: String,
    private val email: String
    )
    

RDBMS와 같은 관계형 데이터베이스는 밸류 타입을 제대로 표현하기 어렵다.

밸류는 불변으로 구현할 것을 권장하며, 이는 엔티티의 밸류 타입 데이터를 변경할 떄는 객체 자체를 완전히 교체한다는 것을 의미한다.
예를 들어 배송지 정보를 변경하는 코드는 기존 객체의 값을 변경하지않고 새로운 객체를 필드에 할당한다.

this.shippingInfo = newShippingInfo

애그리거트

도메인이 커질수록 많은 엔티티와 밸류가 많아지고 모델은 점점 더 복잡해진다.
도메인 모델이 복잡해지면 개발자가 전체 구조가 아닌 한 개 엔티티와 밸류에 집중하는 상황이 발생한다.
이때 상위 수준에서 모델을 관리하지 않고 개별 요소에만 초점을 맞추다보면 큰 수준에서 모델을 이해하지 못해 큰 틀에서 모델을 관리할 수 없는 상황에 빠질 수 있다.

image

도메인 모델에서 전체 구조를 이해하는 데 도움이 되는 것이 애그리거트(Aggregate)이다.
애그리거트는 군집에 속한 객체를 관리하는 루트 엔티티를 갖는다.
루트 엔티티는 애그리거트에 속해있는 엔티티와 밸류 객체를 이용해서 애그리거트가 구현해야 할 기능을 제공한다.
애그리거트를 사용하는 코드는 애그리거트 루트가 제공하는 기능을 실행하고 애그리거트 루트를 통해 간접적으로 애그리거트 내의 다른 엔티티나 밸류 객체에 접근한다.
애그리거트의 내부 구현을 숨겨, 대그리거트 단위로 구현을 캡슐화할 수 있도록 한다.
image

Repository

도메인 객체를 지속적으로 사용하려면 RDBMS, NoSQL, 로컬 파일과 같은 물리적인 저장소에 도메인 객체를 보관해야한다.
리포지터리는 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의한다.

interface OrderRepository {
  fun findByNumber(number: OrderNumber): Order
  fun save(order: Order)
}

OrderRepository의 메서드를 보면 대상을 찾고 저장하는 단위가 애그리거트 루트인 Order인 것을 알 수 있다.
Order는 애그리거트에 속한 모든 객체를 포함하고 있으므로 결과적으로 애그리거트 단위로 저장하고 조회한다.

도메인 모델 관점에서 OrderRepository는 도메인 객체를 영속화하는데 필요한 기능을 추상화한 것으로 고수준 모듈에 속한다.
기반 기술을 이용해 OrderRepository를 구현한 클래스는 저수준 모델로 인프라스트럭처 영역에 속한다.
image

728x90
반응형