하위 도메인과 모델
도메인은 다수의 하위 도메인으로 구성되고, 각 도메인이 다루는 영역은 서로 다르기에, 같은 용어라도 의미가 달라질 수 있다. 카탈로그 도메인의 ‘상품’은 가격, 내용 등을 담지만 배송 도메인의 ‘상품’은 실제 배송되는 물리적인 상품을 의미한다. 모델의 각 요소는 특정 도메인으로 한정할 때 의미가 완전해지기 때문에 각 하위 도메인마다 별도로 모델을 만들어야 한다.
일반적인 애플리케이션 아키텍처는 다음과 같이 네 개의 영역으로 구성된다.
영역 | 설멍 |
---|---|
사용자 인터페이스 또는 표현(Presentation) | 사용자의 요청을 처리하고 사용자에게 정보를 보여준다. 사용자는 사람 뿐 아니라 외부 시스템일 수도 있다. |
응용(Application) | 사용자가 요청한 기능을 실행한다. 업무 로직을 직접 구현하지 않으며 도메인 계층을 조합해서 기능을 실행한다. |
도메인 | 시스템이 제공할 도메인 규칙을 구현 |
인프라스트럭처(infrastrcture) | 데이터베이스나 메시징 시스템 같은 외부 시스템과의 연동을 처리한다. |
도메인 모델이란 용어는 도메인 자체를 표현하는 개념적인 모델을 의미하지만, 도메인 계층을 구현할 때 사용하는 객체 모델에도 도메인 모델이란 용어를 사용한다.
개념 모델과 구현 모델
개념 모델은 순수하게 문제를 분석한 결과물로 DB, 성능, 기술을 고려하지 않는다. 그래서 개념 모델을 구현 가능한 모델로 전환하는 과정을 거치게 된다. 처음부터 완벽한 개념 모델을 만드는 것은 거의 불가능하다. 이는 프로젝트를 진행하면서 해당 도메인을 더 잘 이해하게 되고 다른 의미로 해석하는 경우도 발생하기 때문이다. 그래서 전반적인 개요를 알 수 있는 수준으로 개념 모델을 작성해야 한다. 구현하는 과정에서 개념 모델을 구현 모델로 점진적으로 발전시켜 나가야 한다.
도메인 모델은 크게 엔티티와 밸류로 구분할 수 있다.
아래 예는 ‘돈’이라는 개념을 표현한 Money
밸류 타입이다.
public class Money {
private int value;
// ...
public Money add(Money money) {
return new Money(this.value + money.value);
}
public int getValue() {
return this.value;
}
}
public class OrderLine {
private Money price;
// ...
}
Money.add()
)equals()
와 hashCode()
를 재정의해서 내부 값이 같다면 true
를 반환하도록 구현해야 한다.String
이나 Integer
같은 타입이 아닌 밸류 타입을 사용해서 의미가 잘 드러나도록 할 수도 있다.public Order {
private OrderNo id;
}
setter
는 도메인의 핵심 개념이나 의도를 코드에서 사라지게 한다.
completePayment()
메서드로 여러 제약 사항을 구현해서 처리하는 것과 단순히 setOrderState()
로 상태 값만 변경하는 것만 비교해도 의미 차이는 명확하다.setter
가 있으면 도메인 객체를 생성할 때 온전하지 않은 상태가 될 수도 있다.
아래는 OrderState
enum을 도메인 용어로 작성한 예이다.
public enum OrderState {
PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED
}