도메인 모델
도메인 모델은 특정 도메인을 개념적으로 표현하는 것
도메인을 이해하려면 도메인이 제공하는 기능과 도메인의 주요 데이터 구성을 파악해야 한다.
도메인을 표한하는 방법은 Order, Ship, Pay와 같이 객체로 구별하는 방식과 상태에 따르게 방식이 진행되도록 설계하는 상태 다이어그램을 통해 모델링을 구현할 수 있다.
도메인 모델은 기본적으로 도메인 자체를 이해하기 위한 모델이다.
도메인 모델
일반적인 애플리케이션 아키텍쳐는 4단계 계층으로 구성된다.
Layer | 설명 |
UI | 사용자에게 보여주는 정보 |
Application | 사용자가 요청한 기능이 실행됨 |
도메인 | 시스템이 제공할 도메인의 규칙을 구현 |
Infrastructure | 데이터베이스나 메시징 시스템과 같은 외부 시스템과의 연동을 처리 |
도출한 모델에는 크게 Entity와 Value로 구분할 수 있다.
Entity
- 고유한 식별자를 가진다.
- 주문 도메인에서 각 주문은 주문번호를 가지고 이 주문번호는 각 주문마다 서로 다르다. 이 주문번호가 식별자이다.
- 식별자 생성시에는 특정 규칙에 따라 생성하거나 UUID를 사용하거나 직접 값 입력 또는 일련번호를 사용한다. 이 규칙은 모두 다르다.
public class Order {
private int orderId;
}
Value
개념적으로 완전한 하나를 표현할 때 사용한다. 예를 들어, Value라는 클래스 내부에 valance와 totalUsedValue라는 필드가 있을 때 이 둘은 누가봐도 돈이라는 하나의 개념을 따른다. 이럴 때는 이 두 개를 Money라는 객체를 만들어서 그 자체로의 의미를 완전한 하나로써 표현할 수 있다. 이로써 이 들은 일반적인 integer 타입이 아닌 money라는 하나의 개념적인 의미가 생긴 것으로 코드를 이해하는데 도움이 된다.
public class Money {
private int value;
}
또한 이런 value 타입은 그 자체로써 자체적인 기능을 추가할 수 있다. 세금이 포함된 금액을 확인하고 싶을 때 도메인에서 별도의 작업대신 value 객체에 기능을 추가하여 진행 할 수 있다.
public int getVatValue() {
return this.value + (int) (this.value * 0.1);
}
만약 Value가 절대 변경되어서는 안되는 경우에는 내부에 값을 변경할 수 있는 메소드를 만들지 않아서 immutable하게 선언하면 된다.
도메인 모델에 set 메서드 넣지 않기.
기본적으로 객체를 만들 때 무의식적으로 getter와 setter를 만든다. 습관이다. 아니면 lombok을 통해서 그냥 기본적으로 만들때도 있다. 하지만 setter를 만들게 되는경우 바뀌지 말아야할 값들이 변경되어 문제가 되는 경우가 많다.
예를 들어보면 아래 새로운 계좌를 만든다고 가정해보자.
public void createAccount(int valance, String accountNumber) {
Account account = new Account();
// 잔액 설정
account.setValue(valance);
// 계좌 설정
account.setAccountNumber(accountNumber);
}
계좌 번호를 입력받는 accountNumber가 null인지 확인도 하지 않고 값을 넣게 되면 필수로 들어가야하는 계좌번호 값에 오류가 발생되어 시스템에 문제가 발생한다. 지금은 계좌번호 하나지만 다른 여러 값들을 이런식으로 setter를 통해서 집어넣게 된다면? 일일히 null 체크하기도 번거럽다. 그래서 setter 사용을 못하게 하고 생성자를 통해서 객체를 만들고 이때 잘못된 값으로 데이터를 만들려고 할 때 오류를 뱉어내게 하는 방법을 택하는 것 도 좋다.
// 계좌 생성
public Account createAccount(int valance, String accountNumber) throws Exception {
return new Account(valance, accountNumber);
}
// Money 객체 생성자와 데이터 validation 체크
public Account(int value, String accountNumber) throws Exception {
setValue(value);
setAccountNumber(accountNumber);
}
private void setValue(int value) {
this.value = value;
}
private void setAccountNumber(String accountNumber) throws Exception {
if(StringUtils.isBlank(accountNumber)) throw new Exception("계좌번호가 없습니다.");
this.accountNumber = accountNumber;
}
}
이렇게 하면 외부에서 값을 임의적으로 바꾸지도 못하고 잘못된 값이 들어오는 것을 체크하기에도 좋다.
될 수 있으면 set은 외부에서 사용못하도록 불변된 습성을 가지는게 좋다.
도메인 용어 선정
개발을 진행하다보면 타입을 ENUM에 정의를 하는데 이 때 단순하게 public OrderState { STEP1, STEP2 }로 의미없이 지정하는 경우가 있을 수 있다. 하지만 이렇게 하면 나중에 유지보수나 개발에 어려움이 있기에 public OrderState { SHIPPED, DELIVERING }과 같이 의미있는 말을 적어야 한다.
알맞은 용어를 선택하는 것이 좋은 코드를 작성하는 것 이상으로 중요하다.
출처 : DDD Start 도메인 주도 설계 구현과 핵심 개념 익히기 (출판 지앤선, 저자 최범균)
'DDD' 카테고리의 다른 글
우아한 객체지향 후기 및 정리 (0) | 2019.10.12 |
---|---|
DDD. 응용서비스 코드 규칙과 트랜잭션 관리 (0) | 2019.05.21 |
DDD. 애거리루트 정리 (0) | 2019.05.16 |
DDD. DIP 의존 역전 원칙 (0) | 2019.05.06 |