DDD 도메인 모델, 아키텍처

도메인 모델

  • 특정 도메인을 개념적으로 표현한 것.
  • 아키텍처 상의 도메인 계층을 객체 지향 기법으로 구현하는 패턴.
  • 도메인 계층을 구현할 때 사용하는 객체 모델을 언급할 때 사용됨.

도메인 모델 도출

도메인 모델을 도출할 때 가장 기본이 되는 작업은 모델을 구성하는 핵심 구성요소, 규칙, 기능을 찾는 것

문서화

문서화를 하는 주된 이유는 지식을 공유하기 위함이다. 코드 자체도 문서화의 대상이 된다. 단순히 코드를 보기 좋게 작성하는 것뿐만 아니라 도메인 관점에서 코드가 도메인을 잘 표현해야 가독성이 높아지고 문서로서 코드가 의미를 갖는다.

엔티티와 밸류

이 둘의 차이를 명확하게 이해하는 것은 도메인 구현에 있어 중요하다.

Entity

  • 식별자를 가짐.
  • 식별자는 엔티티 객체마다 고유해서 각 엔티티는 서로 다른 식별자를 가짐.

Value

  • 개념적으로 완전한 하나를 표현할 때 사용.
  • 의미를 좀 더 명확하게 나타낼 수 있음.
  • 데이터 변경 기능을 제공하지 않는 불변 타입으로 표현.
  • 불변 객체로서 참조 투명성과 스레드 안정성을 가짐.
// 기존
public class OrderLine {
    private Product product;
    private int price;
    private int quantity;
    private int amounts;
}

--------------------------------------------------------------
// 변경
// 밸류타입으로 price, amounts 의미를 더 명확하게 나타냄.
public class OrderLine {
    private Product product;
    private Money price;
    private int quantity;
    private Money amounts;
}

public class Money {
    private int value;
    
    ...
    
    // int 대신 Money 타입으로 정의하면 돈 계산을 위한 기능을 추가할 수 있다.
    public Money add(Money money) {
        return new Money(this.value + money.value);
    }
}

엔티티 식별자와 밸류 타입

엔티티 식별자의 실제 데이터는 String과 같은 문자열로 구성된 경우가 많다. 이 때 단순 문자열 대신 밸류 타입을 사용하면 의미를 더 잘 드러낼 수 있다.

public class Order {
    // String 대신 OrderNo 타입을 사용
    private OrderNo id;
    
    ...
}

도메인 모델에서는 set 메서드 넣지 않기

  • set 메서드는 도메인의 핵심 개념이나 의도를 사라지게 한다.
  • set 메서드를 사용하면 도메인 객체를 생성할 때 온전하지 않은 상태가 될 수 있다.
  • 도메인 객체가 불완전한 상태로 사용되는 것을 막으려면 생성자를 통해 필요한 데이터를 받아야 한다. 생성자 호출 시점에 필요한 데이터가 올바른지 검사할 수도 있음.

아키텍처

표현 영역

  • http 요청을 응용 영역이 필요로 하는 형식으로 변환해서 응용 영역에 전달 및 http 응답을 변환하여 전송

응용 영역

  • 시스템이 사용자에게 제공해야 할 기능을 구현.
  • 기능을 구현하기 위해 도메인 영역의 도메인 모델을 사용한다.

도메인 영역

  • 도메인 모델을 구현.

인프라스트럭처 영역

  • 논리적인 개념을 표현하기 보다는 실제 구현을 다룸.
  • DBMS 연동, 메시징 큐 메시지 전송/수신, 몽고DB나 Redis와의 데이터 연동 처리, SMTP를 이용한 메일 발송, http 클라이언트를 이용해서 REST API 호출 등

계층 구조

  • 각 계층 구조는 그 특성상 상위 계층에서 하위 계층으로의 의존만 존재하고 하위 계층은 상위 계층에 의존하지 않는다. 예를 들어 표현, 응용 계층은 도메인 계층에 의존하지만 그 반대는 성립하지 않음.

DIP(의존 역전 원칙)

  • 고수준 모듈(도메인 영역)이 제대로 동작하려면 저수준 모듈(인프라스트럭처 영역)을 사용해야 한다. 그런데 고수준 모듈이 저수준 모듈을 사용하면 구현 변경과 테스트가 어렵다. DIP는 이를 해결하기 위해 저수준 모듈이 고수준 모듈에 의존하도록 바꾼다.
  • 고수준 모듈이 저수준 모듈에 의존하지 않고 구현을 추상화한 인터페이스에 의존하게 한다. 실제 사용할 저수준 구현 객체는 의존 주입을 통해 전달 받는다.
  • 대표적으로 스프링과 같은 프레임워크가 존재함.
  • DIP를 적용할 때 하위 기능을 추상화한 인터페이스는 고수준 모듈 관점에서 도출해야 한다.

도메인 영역 주요 구성요소

요소 설명
ENTITY 고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 가짐. 주문, 회원 같이 도메인의 고유한 개념을 표현함. 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공함.
VALUE 고유의 식별자를 갖지 않는 객체로 주로 개념적으로 하나의 값을 표현할 때 사용됨. 예를들어 주소, 금액 같은 타입.
AGGREGATE 애그리거트는 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것. 예를 들어 Order 엔티티, OrderLine 밸류 객체를 ‘주문’ 애그리거트로 묶을 수 잇다.
REPOSITORY 도메인 모델의 영속성을 처리한다. 예를 들어 DBMS 테이블에서 엔티티 객체를 로딩하거나 저장하는 기능을 제공.
DOMAIN SERVICE 특정 엔티티에 속하지 않은 도메인 로직을 제공. 예를 들어 할인 금액 계산은 상품, 쿠폰, 구매 금액 등 다양한 조건을 이용해서 구현하는데, 이렇게 도메인 로직이 여러 엔티티와 밸류를 필요로 하면 도메인 서비스에서 로직을 구현함.

모듈 구성

  • 아키텍처의 각 영역은 별도 패키지에 위치한다. 패키지 구성 규칙에 정답이 존재하는 것은 아니지만 영역별로 모듈이 위치할 패키지를 구성할 수 있다.
  • 도메인 모듈은 도메인에 속한 애그리거트를 기준으로 다시 패키지를 구성한다.
Reference
  • 도메인 주도 개발 시작하기, 최범균 지음

Categories:

Updated:

Comments