도메인이란?
다시, 도메인의 정의는 무엇일까?
도메인은 개발자가 구현으로 해결하고자 하는 문제 영역이다.
개발을 하기 앞서, 기획과 기능을 정한다.
이는 개발자가 직접 정할수도있고, 또는 기획자나 stakeholder에 의해 정해질 수 있다.
도메인은 이들이 공통적으로 집중할 수 있는 소통창구 역할을 한다.
도메인 모델 패턴
일반적인 어플리케이션의 아키텍쳐는 다음과 같다.
표현(UI) | 사용자의 요청 처리 |
응용(Service) | 사용자가 요청한 기능 실행, 비즈니스 로직을 직접 구현하지 않고 도메인 계층을 조합하여 기능을 실행 |
도메인 | 도메인 규칙 구현 |
인프라스트럭쳐 | DB나 MQ같은 외부 시스템과의 연동을 처리 |
도메인 계층은 도메인의 핵심 규칙을 구현한다.
도메인의 핵심 규칙은, '출고 전에 배송지를 변경할 수 있다' 나 '주문 취소는 배송 전에만 할 수 있다' 등의 규칙이다.
위의 규칙을 구현한 코드는 다음과 같다.
public class Order{
private OrderState state;
private ShipppingInfo shippingInfo;
public void changeShippingInfo(ShippingInfo newShippingInfo){
if(!isShippingChangeable())
throw new IllegalStateException();
this.shippingInfo = newShippingInfo;
}
private boolean isShippingChangeable(){
return state == OrderState.PAYMENT_WAITING ||
state == OrderState.PREPARING;
}
}
public enum OrderState{
PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED;
}
배송지교환 가능 여부를 반환하는 함수는 Order 클래스에 구현이 되어있지만, OrderState로도 옮길 수 있다.
중요한것은 도메인 업무 규칙을 주문 도메인 모델인 Order나 OrderState에서 구현한다는 점이다.
규칙이 바뀌거나 규칙을 확장해야 할 때 응용 계층에 영향을 덜 주고 변경에만 집중할 수 있다는 장점이 있다.
즉, Order라는 도메인을 사용하는 서비스 레이어에서, 핵심 규칙을 구현한 함수를 가지고 있다면
이는 DDD 관점에서 벗어난 방식이다.
엔티티와 밸류
도메인 모델은 엔티티와 밸류로 구분할 수 있다.
엔티티
JPA를 공부하면서, 엔티티라는 말을 처음 들어보았다.
엔티티는 DB테이블에 저장되어있는 정보를 클래스 객체로 옮긴 것이다.
엔티티의 중요한 특성으로는 identifier, 식별자를 가진다는것인데
보통 uuid나 autoincrement 같은 식별자를 사용한다.
밸류
그렇다면 밸류는 무엇일까?
책에서는 밸류 타입은 하나의 클래스(엔티티)에서 여러 개의 필드 중, 필드들의 조합으로
개념적으로 완전한 하나를 표현할때 사용된다고 한다.
public class ShippingInfo{
private String receiverName;
private String receiverPhoneNumber;
private String shippingAddress1;
private String shippingAddress2;
private String shippingZipcode;
}
여기에서 밸류는 receiver, address라고 생각할 수 있다.
public class ShippingInfo{
private Receiver receiver;
private Address address;
}
public class Receiver{
private String name;
private String phoneNumber;
}
public class Address{
private String address1;
private String address2;
private String zipcode;
}
밸류 타입을 사용함으로써, 도메인을 개념적으로 완전한 하나로 잘 표현할 수 있게 되었다.
이 외에도 의미를 명확하게 표현하기 위해 밸류 타입을 사용하는 경우도 있는데
public class OrderLine{
private Product product;
private int price;
private int amounts;
private int quantity;
}
public class Order{
private string orderId;
//...
}
이 코드에서 밸류 타입을 사용하면 다음과 같이 바뀐다.
public class OrderLine{
private Product product;
private Money price;
private Money amounts;
private int quantity;
}
public class Order{
private OrderNo id;
//...
}
public class Money{
private int value;
public Money add(Money money){
return new Money(this.value + money.value);
}
public Money multiply(int multiplier){
return new Money(value * multiplier);
}
}
string 타입의 주문 번호는 OrderNo이라는 타입으로 바뀌어 주문 번호라는걸 더 쉽게 알 수 있다.
int 타입 대신에 Money라는 밸류 타입을 사용함으로써, 돈 계산이라는 의미 전달이 더 명확하게 된다.
이때, add 함수를 보면 this의 value를 수정하는것이 아닌, 새로운 객체를 return하게 되는데
이는 Immutable함을 보장해준다.
Immutable이 안지켜 진다면?
Immutable한 객체는 한번 생성되면 필드를 직접 수정하는것이 불가능하다.
만약 setter를 제공해 mutable하게 만든다면,
Order order = new Order();
// setter로 값 전달
order.setOrderLine(lines);
order.setShippingInfo(shippingInfo);
// order.setReceiverInfo(receiverInfo); 실수로 빼먹었다!
order.setState(OrderState.PREPARING);
주문자 설정을 누락하고 있지만 준비 단계로 바뀌게 된다.
Money price = new Money(1000)
OrderLine line = new OrderLine(product, price, 2)
price.setValue(2000) // 참조 투명성 문제 발생!
또한 OrderLine의 price 값이 잘못 반영될 수 있다.
이렇게 코드 관리에 어려움을 주게 된다.
따라서 생성자에 private한 setter를 두어 immutable 객체로 만드는것이 좋다.
출처 : https://www.hanbit.co.kr/store/books/look.php?p_code=B4309942517 [도메인 주도 개발 시작하기, 최범균 저]
'Back-End > DDD' 카테고리의 다른 글
[DDD] 2. DDD의 아키텍쳐 (0) | 2023.04.01 |
---|---|
[DDD] 0. DDD 시작하기 (0) | 2023.03.31 |