구현해야 할 소프트웨어의 대상이 온라인 서점이라면,
'온라인 서점' 은 소프트웨어로 해결하고자 하는 문제 영역, 즉 Domain에 해당된다.
한 도메인은 다시 하위 도메인(주문, 회원, 결제, 배송, 정산, 카탈로그 등)으로 나눌 수 있다.
카탈로그 하위 도메인은 고객에게 구매할 수 있는 상품 목록을 제공하고, 주문 하위 도메인은 고객의 주문을 처리한다. 배송 하위 도메인은 고객에게 구매한 상품을 전달하는 일련의 과정을 처리한다.
이렇게 한 하위의 도메인은 다른 하위 도메인과 연동하여 완전한 기능을 제공한다.
'고객이 물건을 구매' 하면 '주문, 결제, 배송, 혜택' 하위 도메인의 기능이 엮이게 된다.
특정 도메인을 위한 소프트웨어라고 해서 도메인이 제공해야 할 모든 기능을 구현하는 것은 아니다. 배송 시스템을 자체 구축하기보단 외부 배송업체의 시스템을 사용하고, 배송추적에 필요한 기능만 일부 연동한다.
도메인 모델은 특정 도메인을 개념적으로 표현한 것이다.
도메인 모델을 사용하면 여러 관계자들이 동일한 모습으로 도메인을 이해하고 도메인 지식을 공유하는 데 도움이 된다.
아래 사진은 객체를 이용한 도메인 모델이다. 주문 모델을 객체 모델로 구성하면 다음과 같이 만들 수 있다.
도메인을 이해하려면 도메인이 제공하는 기능과 도메인의 주요 데이터 구성을 파악해야 하는데, 이런 면에서 기능과 데이터를 함께 보여주는 객체 모델은 도메인 모델링하기에 적합하다.
도메인 모델은 객체로만 모델링할 수 있는 것은 아니고, 상태 다이어그램을 이용해서 주문의 상태 전이를 모델링할 수 있다.
클래스 다이어그램, 상태 다이어그램과 같은 UML 표기법만 사용해야하는 것은 아니다.
도메인을 이해하는 데 도움이 된다면 표현 방식이 무엇인지는 중요하지 않다.
도메인 모델은 기본적으로 도메인 자체를 이해하기 위한 개념 모델이다. 개념 모델을 이용해서 바로 코드를 작성할 수 있는 것은 아니기에 구현 기술에 맞는 구현 모델이 따로 필요하다. 개념 모델과 구현 모델은 서로 다른 것이지만 구현 모델이 개념 모델을 최대한 따르도록 할 수는 있다.
일반적으로 애플리케이션의 아키텍처는 네 개의 계층으로 구성된다.
계층(Layer) | 설명 |
---|---|
사용자인터페이스(UI) 또는 표현(Presentation) | 사용자의 요청을 처리하고 사용자에게 정보를 보여준다. 여기서 사용자는 소프트웨어 사용하는 사람뿐만 아니라 외부 시스템도 사용자가 될 수 있다. |
응용(Application) | 사용자가 요청한 기능을 실행한다. 업무 로직을 직접 구현하지 않으며 도메인 계층을 조합해서 기능을 실행한다. |
도메인(Domain) | 시스템이 제공할 도메인의 규칙을 구현한다. |
인프라스트럭쳐 (Infrastructure) | 데이터베이스나 메시징 시스템과 같은 외부 시스템과의 연동을 처리한다. |
도메인 모델은 아키텍처상의 도메인 계층을 객체 지향 기법으로 구현하는 패턴을 말한다.
도메인 계층은 도메인의 핵심 규칙을 구현한다.
주문 도메인의 경우 '출고 전에 배송지를 변경할 수 있다' 는 규칙과 '주문 취소는 배송 전에만 할 수 있다' 는 규칙을 구현한 코드가 도메인 계층에 위치하게 된다. 이런 도메인 규칙을 객체 지향 기법으로 구현하는 패턴이 도메인 모델 패턴이다.
이 코드는 주문 도메인의 일부 기능을 도메인 모델 패턴으로 구현한 것이며,
주문 대기 중(PAYMENT_WAITING), 상품 준비 중(PREPARING) 단계에서 배송지를 변경 할 수 있다는 도메인 규칙을 구현하고 있다.
data class Order(
val state: OrderState,
val shippingInfo: ShippingInfo
) {
fun changeShippingInfo(newShippingInfo: ShippingInfo) {
if(!state.isShippingChageable())
throw IllegalStateException("can't change shipping in" + state)
this.shippingInfo = newShippingInfo
}
private fun isShippingChangeable(): Boolean {
return state == OrderState.PAYMENT_WAITING || state == OrderState.PREPARING
}
}
enum class OrderState {
PAYMENT_WAITING,
PREPARING,
SHIPPED, DELIVERING, DELIVERY_COMPLETED
}
주문과 관련된 중요 업무 규칙을 주문 도메인 모델인 Order나 OrderState에서 구현한다. 핵심 규칙을 구현한 코드는 도메인 모델에만 위치하기 때문에 규칙이 바뀌거나 규칙을 확장해야 할 때 다른 코드에 영향을 덜 주고 변경 내역을 모델에 반영할 수 있게 된다.
개념 모델은 순수하게 문제를 분석한 결과물이다. 개념 모델은 데이터베이스, 트랜잭션 처리, 성능, 구현 기술과 같은 것들을 고려하고 있지 않기 때문에 실제 코드를 작성할 때 개념 모델을 있는 그대로 사용할 수 없다. 그래서 개념 모델을 구현 가능한 형태의 모델로 전환하는 과정을 거친다. 처음부터 완벽하게 개념 모델을 만들면 좋지만 현실적으로 불가능하다. 따라서 프로젝트 초기에는 전반적인 개요를 알 수 있는 수준의 개념 모델로 도메인에 대한 전체 윤곽을 이해하는 데 집중하고, 구현 과정에서 개념 모델을 구현모델로 점진적으로 개선해 나가야 한다.