도메인이란?
그림 1.1 도메인은 여러 하위 도메인으로 구성된다.
도메인마다 고정된 하위 도메인이 존재하는것은 아니며 어떻게 구성할지 여부는 상황에 따라 달라진다. 회원, 주문, 리뷰 등은 자체 시스템을 사용하고 결제(외부 PG), 배송(외부 물류)를 사용하는 것처럼.
도메인 모델에는 다양한 정의가 존재한다. 기본적으로 도메인 모델은 특정 도메인을 개념적으로 표현한 것이다.
그림 1.3 객체 기반 주문 도메인 모델
그림 1.4 상태 다이어그램을 이용한 주문 상태 모델링
일반적인 애플리케이션의 아키텍처 네 개의 영역
도메인 계층은 도메인의 핵심 규칙을 구현한다.
public class Order {
private OrderState state;
private ShippingInfo shippingInfo;
public void changeShippingInfo(ShippingInfo newShippingInfo) {
if (!state.isShippingChangeable()) {
throw new IllegalStateException("can't change shipping in " + state);
}
this.shippingInfo = newShippingInfo;
}
public void changeShipped() {
// 로직 검사
this.state = OrderState.SHIPPED;
}
...
}
public enum OrderState {
PAYMENT_WAITING {
public boolean isShippingChangeable() {
return true;
}
},
PREPARING {
public boolean isShippingChangeable() {
return true;
}
},
SHIPPED, DELIVERING, DELIVERY_COMPLETED;
public boolean isShippingChangeable() {
return false;
}
}
주문 도메인의 일부 기능을 도메인 모델 패턴으로 구현한 것이다.
중요한 점은 주문과 관련된 중요 업무 규칙을 주문 도메인 모델인 Order나 OrderState에서 구현한다는 점이다.
핵심 규칙을 구현한 코드는 도메인 모델에만 위치하기 때문에 규칙이 바뀌거나 규칙을 확장해야 할 때 다른 코드에 영향을 덜 주고 변경 내역을 모델에 반영할 수 있게 된다.
개념 모델과 구현 모델
천재 개발자라 할지라도 도메인에 대한 이해 없이 코딩을 시작할 수는 없다. 기획서, 유스 케이스, 사용자 스토리와 같은 요구사항과 관련자와의 대화를 통해 도메인을 이해하고 이를 바탕으로 도메인 모델 초안을 만들어야 비로소 코드를 작성할 수 있다.
주문 도메인과 관련된 몇가지 요구사항을 보자.
public class Order {
public void changeShipped() { ... }
public void changeShippingInfo(ShippingInfo newShipping) { ... }
public void cancel() { ... }
public void completePayment() { ... }
}
Order에 관련 기능 메서드
public class OrderLine {
private Product product;
private int price;
private int quantity;
private int amount;
...
}
요구사항 추가.
두 요구사항에 따르면 주문할 상품, 가격, 구매 개수를 포함하고 있어야 한다.
public class Order {
private List<OrderLine> orderLines;
private int totalAmounts;
private void setOrderLines(List<OrderLine> orderLines) { ... }
private void verifyAtLeastOneOrMoeOrderLines(List<OrderLine> orderLines) { ... }
private void calculateTotalAmounts() { ... }
...
}
Order와 OrderLine의 관계
public class ShipingInfo {
private String receiverName;
private String receiverPhoneNumber;
private String shipingAddress1;
private String shipingAddress2;
private String shipingZipcode;
...
}
배송지 정보 이름, 번화번호, 주소
public class Order {
private List<OrderLine> orderLines;
private int totalAmounts;
private ShipingInfo shippingInfo;
private void setOrderLines(List<OrderLine> orderLines) { ... }
private void verifyAtLeastOneOrMoeOrderLines(List<OrderLine> orderLines) { ... }
private void calculateTotalAmounts() { ... }
private void setShippingIngo(ShippingInfo shippingInfo) { ... }
...
}
public class Order {
private OrderState state;
public void cancel() { ... }
public void verifyNotYetShipped() { ... }
}
도메인을 구현하다 보면 특정 조건이나 상태에 따라 제약이나 규칙이 달리 적용되는 경우가 많다.
지금까지 주문과 관련된 요구사항에서 도메인 모델을 점진적으로 만들어 나갔다. 요구사항 정을 위해 도메인 전문가나 다른 개발자와 논의하는 과정에서 공유하기도 한다. 모델을 공유할 때는 화이트보드나 위키와 같은 도구를 사용해서 누구나 쉽게 접근할 수 있도록 하면 좋다.
문서화를 하는 주된 이유는 지식을 공유하기 위함이다. 단순히 코드를 보기 좋게 작성하는 것뿐만 아니라 도메인 관점에서 코드가 도메인을 잘 표현해야 비로소 코드의 가독성이 높아지고 문서로서 코드가 의미를 갖는다.