Chapter1) 도메인 모델 시작하기

dbstmd·2023년 11월 5일
0

DDD

목록 보기
1/8

1.1 도메인이란?

도메인이란?

  • 특정 문제 영역 또는 비즈니스 분야를 가르키는 개념
  • 온라인 서점 예시
    온라인 서점 소프트웨어는 온라인으로 책을 판매하는 데 필요한 상품조회, 구매, 결제, 배송 추적 등의 기능을 제공해야 한다. 이때, '온라인 서점'은 소프트웨어로 해결하고자 하는 문제 영역, 즉 도메인에 해당된다.

그림 1.1 도메인은 여러 하위 도메인으로 구성된다.

그림 1.1 도메인은 여러 하위 도메인으로 구성된다.

도메인마다 고정된 하위 도메인이 존재하는것은 아니며 어떻게 구성할지 여부는 상황에 따라 달라진다. 회원, 주문, 리뷰 등은 자체 시스템을 사용하고 결제(외부 PG), 배송(외부 물류)를 사용하는 것처럼.

1.2 도메인 전문가와 개발자 간 지식 공유

  • 전문가는 해당 도메인에 대한 지식과 경험을 바탕으로 본인들이 원하는 기능 개발을 요구한다.
  • 코딩에 앞서 요구사항을 오바르게 이해하는 것이 중요하다.
  • 가장 간단하고 효율적인 방법은 개발자와 전문가가 직접 대화하는 것.

1.3 도메인 모델

도메인 모델에는 다양한 정의가 존재한다. 기본적으로 도메인 모델은 특정 도메인을 개념적으로 표현한 것이다.

그림 1.3 객체 기반 주문 도메인 모델

  • 도메인 모델을 사용하면 여러 관계자들이 동일한 모습으로 도메인을 이해하고 도메인 지식을 공유하는 데 도움이 된다.
  • 도메인을 이해하려면 도메인이 제공하는 기능과 도메인의 주요 데이터 구성을 파악해야 한다.
  • 도메인 모델을 객체로만 모델링 할 수 있는 것은 아니다. 상태 다이어그램을 이용해서 주문의 상태 전이를 모델링할 수 있다.

그림 1.4 상태 다이어그램을 이용한 주문 상태 모델링

  • 도메인을 이해하는 데 도움이 된다면 표현 방식이 무엇인지는 중요하지 않다.
  • 도메인 모델은 도메인 자체를 이해하기 위한 개념 모델이다. 개념 모델을 이용해서 바로 코드를 작성할 수 있는 것은 아니기에 구현 기술에 맞는 구현 모델이 따로 필요하다. 그렇기에 개념 모델과 구현 모델은 서로 다른 것이지만 구현 모델이 개념 모델을 최대한 따르도록 할 수는 있다.

1.4 도메인 모델 패턴

일반적인 애플리케이션의 아키텍처 네 개의 영역

  • 사용자 인터페이스(UI) : 사용자의 요청을 처리하고 사용자에게 정보를 보여준다.
  • 응용 : 사용자가 요청한 기능을 실행한다. 업무 로직을 직접 구현하지 않으며 도메인 계층을 조합해서 기능을 실행한다.
  • 도메인 : 시스템이 제공할 도메인의 규칙을 구현한다.
  • 인프라스트럭처 : 데이터베이스나 메시징 시스템과 같은 외부 시스템과의 연동을 처리한다.

도메인 계층은 도메인의 핵심 규칙을 구현한다.

  • 주문 도메인의 경우 '출고 전에 배송지를 변경할 수 있다'는 규칙과 '주문 취소는 배송 전에만 할 수 있다'는 규칙을 구현한 코드가 도메인 계층에 위치하게 된다.
  • 이런 도메인 규칙을 객체 지향 기법으로 구현하는 패턴이 도메인 모델 패턴이다.
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;
	}
}

주문 도메인의 일부 기능을 도메인 모델 패턴으로 구현한 것이다.

  • 이 코드는 주문의 상태에 따라 배송지를 변경하는 로직을 구현하였다.
  • 큰 틀에서 보면 OrderState는 Order에 속한 데이터이므로 배송지 정보 변경 가능 여부를 판단하는 코드를 Order로 이동할 수도 있다.

중요한 점은 주문과 관련된 중요 업무 규칙을 주문 도메인 모델인 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() { ... }
}

도메인을 구현하다 보면 특정 조건이나 상태에 따라 제약이나 규칙이 달리 적용되는 경우가 많다.

  • 출고를 하면 배송지 정보를 변경할 수 없다.
  • 출고 전에 주문을 취소할 수 있다.
  • 고객이 결제를 완료하기 전에는 상품을 준비하지 않는다.

지금까지 주문과 관련된 요구사항에서 도메인 모델을 점진적으로 만들어 나갔다. 요구사항 정을 위해 도메인 전문가나 다른 개발자와 논의하는 과정에서 공유하기도 한다. 모델을 공유할 때는 화이트보드나 위키와 같은 도구를 사용해서 누구나 쉽게 접근할 수 있도록 하면 좋다.

문서화를 하는 주된 이유는 지식을 공유하기 위함이다. 단순히 코드를 보기 좋게 작성하는 것뿐만 아니라 도메인 관점에서 코드가 도메인을 잘 표현해야 비로소 코드의 가독성이 높아지고 문서로서 코드가 의미를 갖는다.

참고) https://www.slideshare.net/madvirus/ddd-71165031

profile
개인 학습용

0개의 댓글