DDD(Domain-Driven Design)는 도메인과 일치하도록 소프트웨어를 모델링하는 데 중점을 둔 소프트웨어 설계 접근 방식이다. (feat. 위키백과)
이번 글을 포함한 DDD 관련 글에서, “온라인 서점”이라는 도메인을 예시로 들어서 설명하겠다.
도메인
- 도메인 = 소프트웨어로 해결하고자 하는 문제 영역
- 개발자 입장에서 바라보면 온라인 서점은 구현해야 할 소프트웨어의 대상, 즉 도메인에 해당한다.
- 한 도메인은 다시 하위 도메인으로 나눌 수 있다. 한 하위 도메인은 다른 하위 도메인과 연동하여 완전한 기능을 제공한다.
- 온라인 서점 도메인은 위와 같이 몇 개의 하위 도메인으로 나눌 수 있다.
- 주문 도메인은 고객의 주문을 처리하고, 카탈로그 도메인은 고객에게 구매할 수 있는 상품 목록을 제공한다.
- 특정 도메인을 위한 소프트웨어라고 해서 도메인이 제공해야 할 모든 기능을 직접 구현하는 것은 아니다.
- 많은 온라인 쇼핑몰이 배송 도메인의 일부 기능은 자체 시스템으로 구현하고, 나머지 기능은 외부 업체의 시스템을 사용한다. 결제 시스템도 직접 구현하기보다는 결제 대행업체를 이용해서 처리할 때가 많다.
- 온라인 서점 도메인은 위와 같이 몇 개의 하위 도메인으로 나눌 수 있다.
도메인 모델
정의 1 : 도메인 모델 (개념 모델) = 도메인 자체를 이해하기 위한 모델
- 위와 같이 클래스 다이어그램(UML 표기법)을 사용하여 주문 도메인의 도메인 모델을 표현할 수 있다.
- 주문 도메인을 생각해보자. 온라인 서점에서 주문을 하려면 상품을 몇 개 살지 선택하고 배송지를 입력한다. 선택한 상품 가격을 이용해서 총 지불 금액을 계산하고, 금액 지불을 위한 결제 수단을 선택한다. 주문한 뒤에도 배송 전이면 배송지 주소를 변경하거나 주문을 취소할 수 있다. 이를 위한 주문 도메인 모델을 객체로 모델링한 것이 위 그림이다.
- 도메인 모델을 객체로만 모델링할 수 있는 것은 아니며, UML 표기법만 사용해야 하는 것도 아니다. 도메인을 이해하는 데 도움이 된다면 표현 방식이 무엇인지는 중요하지 않다.
- 주문 도메인의 경우에는 상태 다이어그램을 사용하여 주문의 상태 전이를 모델링할 수도 있다. 또한, 관계가 중요한 도메인이라면 그래프를 이용해서 도메인을 모델링할 수 있고, 계산 규칙이 중요하다면 수학 공식을 활용해서 도메인 모델을 만들 수도 있다.
- 각 하위 도메인마다 별도로 도메인 모델을 만들어야 한다. 각 하위 도메인이 다루는 영역은 서로 달라서 같은 용어라도 하위 도메인마다 의미가 달라질 수 있기 때문이다.
- 카탈로그 도메인의 상품이 상품 가격, 상세 내용을 담고 있는 정보를 의미한다면 배송 도메인의 상품은 고객에게 실제 배송되는 물리적인 상품을 의미한다.
- 처음부터 완벽한 개념 모델을 만들기보다는 전반적인 개요를 알 수 있는 수준으로 개념 모델을 작성하고, 구현하는 과정에서 개념 모델을 구현 모델로 점진적으로 발전시켜 나가야 한다.
- 소프트웨어를 개발하는 동안 개발자와 관계자들은 해당 도메인을 더 잘 이해하게 된다. 또한, 프로젝트 초기에 이해한 도메인 지식이 시간이 지나 새로운 통찰을 얻으면서 완전히 다른 의미로 해석되는 경우도 있다. 즉, 프로젝트 초기에 완벽한 도메인 모델을 만들더라도 결국 도메인에 대한 새로운 지식이 쌓이면서 모델을 보완하거나 변경하는 일이 발생한다.
- 도메인 모델을 사용하면 여러 관계자들이 동일한 모습으로 도메인을 이해하고 도메인 지식을 공유하는 데 도움이 된다.
정의 2 : 도메인 모델 (구현 모델) = 도메인 계층의 객체 모델
- 일반적인 애플리케이션의 아키텍처는 위와 같이 네 개의 영역으로 구성되며, 도메인 계층은 도메인 규칙을 구현한다. 도메인 규칙이란 도메인에서 반드시 지켜야 하는 제약 조건이나 로직을 의미하며, 도메인 계층의 주요 구성요소는 다음과 같다.
- 엔티티
- 밸류
- 애그리거트
- 리포지토리
- 도메인 서비스
- 도메인 모델은 도메인 모델 패턴을 의미하며, 도메인 모델 패턴은 아키텍처 상의 도메인 계층을 객체 지향 기법으로 구현하는 패턴을 말한다. (feat. 마틴 파울러의 “엔터프라이즈 애플리케이션 아키텍처 패턴”)
// 엔티티
public class Order {
private String orderNumber;
private OrderState state;
private ShippingInfo shippingInfo;
public void changeShippingInfo(ShippingInfo newShippingInfo) {
if (!isShippingChangeable()) {
throw new IllegalStateException("Can't change shipping in" + state);
}
this.shippingInfo = newShippingInfo;
}
private boolean isShippingChangeable() {
return state == OrderState.PAYMENT_WAITING ||
state = OrderState.PREPARING;
}
}
public enum OrderState {
PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED;
}
- 위와 같이 Java 코드로 주문 도메인의 도메인 모델(엔티티)을 구현할 수 있다.
- Order 클래스는 주문 도메인 모델의 엔티티이며, 주문 도메인의 “출고 전에 배송지를 변경할 수 있다”라는 도메인 규칙을 구현하고 있다.
- 어떤 도메인과 관련된 중요 업무 규칙은 해당 도메인 모델에서 구현한다. 도메인 규칙을 구현한 코드는 도메인 모델에만 위치하기 때문에, 규칙이 바뀌거나 규칙을 확장해야 할 때 다른 코드에 영향을 덜 주고 변경 내역을 도메인 모델에 반영할 수 있게 된다.
- 주문 도메인의 경우, 주문과 관련된 중요 업무 규칙을 주문 도메인 모델인 Order나 OrderState 등에서 구현한다.
도메인 모델 정의에 대한 나의 요약
도메인 모델에 대한 나의 생각을 정리한 내용이다. 누군가 나에게 “도메인 모델이 무엇인가요?”라고 물어본다면, 아래와 같이 이야기할 것이다.
- 도메인 모델은 일반적으로 도메인 자체를 이해하기 위한 모델을 의미하며, “개념 모델”이다. 이때, 조직의 구성원들이 이러한 도메인 모델(개념 모델)을 보고 도메인을 이해할 수 있다면, 도메인 모델(개념 모델)을 꼭 클래스 다이어그램으로 나타낼 필요는 없다.
- 조직의 궁극적인 목표는 소프트웨어를 개발하는 것이고, 이는 도메인 모델(개념 모델)을 구현해야 하는 것을 의미한다. 객체 지향 프로그래밍을 기반으로 개발한다면, 도메인 모델(개념 모델)의 구현은 객체(클래스) 형태가 된다. 따라서, 도메인 모델(개념 모델)을 꼭 클래스 다이어그램으로 나타낼 필요는 없지만, 객체 지향 프로그래밍을 기반으로 개발하는 개발자 입장에서 도메인 모델(개념 모델)이 클래스 다이어그램으로 나타내져 있다면 압도적으로 구현하기 편리하다.(개념 모델 그대로 코드로 구현하면 되기 때문이다.)
- 요약하자면, 객체 지향 프로그래밍을 기반으로 소프트웨어를 개발하는 조직에서 도메인 모델(개념 모델)은 도메인을 객체로 모델링한 것, 즉 객체 기반 도메인 모델을 의미한다고 볼 수 있다!
도메인 모델의 구성요소
- 객체 기반 도메인 모델의 구성요소는 다음과 같다.
- 엔티티
- 밸류
- 애그리거트
- 리포지토리 -> 구현을 위한 도메인 모델
- 도메인 서비스
- 참고 자료
도메인 주도 개발 시작하기(저자: 최범균)