본 게시물은 [도메인 주도 개발 시작하기] 를 읽고 정리 한 글입니다.

1. 도메인이란?

도메인은 소프트웨어로 해결하고자하는 문제 영역이다. 또한 하나의 도메인은 다시 하위 도메인으로 나눌 수 있다.

온라인 서점을 예시로 들어보자. 온라인 서점은 책을 판매하기 위해서 상품 조회, 구매, 결제, 배송 등의 기능을 제공해야한다. 따라서 온라인 서점 소프트웨어가 하나의 큰 도메인이되고 제공해야할 기능들을 하위 도메인으로 볼 수 있다.

image

이때 하위 도메인을 구성하는 방식은 상황에 따라 달라진다. 모든 소프트웨어가 리뷰나 혜택 도메인을 필요로 하지 않기 때문이다.

image

2. 도메인 모델

기본적으로 도메인 모델은 특정 도메인을 개념적으로 표현한 것이다. 온라인 쇼핑몰에서 주문 도메인을 예시로 설명해보자.

주문 도메인은 상품의 개수를 정하고 배송지를 입력한다. 선택한 상품의 가격으로 총 지불 금액을 계산하고 결제 수단을 선택한다. 아직 배송 전이라면 주문한 상품에 대해서 취소하거나 배송지 변경이 가능해야한다.

image

위 그림의 모델은 객체를 이용한 도메인으로, 도메인의 여러 기능들을 확인할 수 있다.. Order 는 totalAmounts 를 가지고 있고, changeShipping() 이나 cancel() 이 가능하다.

위와 같이 도메인 모델을 사용하면 여러 관계자들이 동일한 모습으로 도메인을 이해하고 공유할 수 있다. 특히 객체를 이용한 도메인은 기능과 데이터를 함께 보여줄 수 있다는 장점이 있다.

image

이외에도 상태 다이어드램을 이용한 도메인 모델링이 가능하다. 이는 상품 준비 중 상태에서 주문을 취소하면 결제 취소가 함께 이루어지는 것을 쉽게 파악할 수 있다. 말 그대로 상태의 변화를 파악하기 쉬운 모델링이다.

도메인 모델을 표현할 대는 UML 표기법 뿐만 아니라 필요에 따라서 그래프를 이용해 모델링할 수도 있고, 수학 공식을 활용할 수도 있다. 결국 도메인 모델은 기본적으로 도메인 자체를 이해하기 위한 개념 모델이지 구현을 위한 모델이 아니다. 따라서 표현 방법은 굉장히 유연하게 적용할 수 있으며 구현을 위한 구현 모델이 따로 필요하다. 이때 구현 모델이 개념 모델을 최대한 따르도록 할 수는 있지만 여전히 유연하다.

1) 하위 도메인과 모델

  • 도메인은 다수의 하위 도메인으로 구성된다.
  • 각 하위 도메인은 서로 다른 영역을 다루기에 같은 용어라도 하위 도메인마다 의미가 달라질 수 있다.
  • 즉, 여러 하위 도메인을 하나의 다이어그램에 모델링하면 안 된다!
    • 모델의 각 구성요소는 특정 도메인으로 한정되어야하므로 각 하위 도메인마다 별도의 모델이 필요하다.

3. 도메인 모델 패턴

일반적인 애플리케이션의 아키텍처는 아래와 같이 네 개의 영역으로 구성된다.

image

image

앞선 도메인 모델은 도메인 자체를 이해하는 개념 모델이라면, 지금부터의 도메인 모델은 마틴 파울러가 쓴 『엔터프라이즈 애플리케이션 아키텍처 패턴』(위키북스, 2015) 책의 도메인 모델 패턴을 의미한다. 도메인 모델은 아키텍처 상의 도메인 계층을 객체 지향 기법으로 구현하는 패턴을 말한다.

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

출고 전에 배송지를 변경할 수 있다는 규칙과 주문 취소는 배송 전에만 할 수 있다는 규칙을 구현해보자.

public class Order {
	...
	if (!state.isShippingChangeable())
	...
}

public enum OrderState{
	PREPARING{
		public boolean isShippingChangeable(){
			return ture;
		}
	}
	...
}

위는 주문 도메인의 일부를 도메인 모델 패턴으로 구현하는 방법이다. 주문 상태를 표현하는 OrderState는 주문 정보 변경 가능 여부를 반환하는 isShippingChangeable() 메서드를 제공한다.

2) 상위 데이터에서 규칙을 구현할 수 있다.

OrderState 가 아닌 Order 에서 주문 정보 변경 여부 로직을 구현할 수 있다.

public class Order{
	...
	if (state == OrderState.PREPARING ... )
}

public enum OrderState{
	PREPARING, ...
}

핵심은 주문과 관련된 로직은 주문 도메인 모델인 Order 나 OrderState 에 구현되어야한다는 점이다. 해당 로직은 도메인 모델에만 위치하기에 다른 도메인에 영향을 덜 줄 수 있다.

4. 도메인 모델 도출

도메인의 도출은 요구사항에서부터 출발한다. 요구사항을 통해 기능, 데이터 구성, 도메인과의 관계, 제약 내용을 확인할 수 있다.

1) 기능

주문에 관한 기능들을 정리한다. 이를 통해 Order 에 관련 기능을 메서드로 추가할 수 있다.

2) 데이터 구성

주문 항목을 표현하는 OrderLine 이 있을 때 이를 표현하기 위한 데이터를 파악할 수 있다. 예를 들어, 주문할 상품, 가격, 개수 등을 파악하여 필드로 추가할 수 있다.

3) 도메인과의 관계

위에서 구성한 Order 와 OrderLine 이 어떤 관계인지 파악한다. Order는 OrderLine 을 하나 이상 포함해야하고 총 주문 금액은 OrderLine 에서 구할 수 있다 등이다. 이때 하나 이상 포함하기 위해 따로 verifyAtLeastOneMoreOrderLines() 라는 메서드를 하나 만들어 OrderLines 가 하나 이상인 지 확인하는 과정을 추가할 수 있다.

4) 제약

주문 상태는 출고 이전에만 변경할 수 있다 등의 제약을 도메인에 추가할 수 있다.

5 엔티티와 밸류

요구사항에서 도출된 도메인 모델은 엔티티와 밸류로 나뉜다.

1) 엔티티

엔티티의 가장 큰 특징은 식별자를 가지는 것이다. DB 에서 id 값을 가지는 것이라고 생각할 수 있다. 주문 도메인에서는 각 주문이 주문 번호를 가지기에 엔티티라 말할 수 있다. 엔티티의 식별자는 바뀌지 않고 고유하기에 두 엔티티 객체는 식별자를 통해 같고 다름을 판단할 수 있다.

엔티티의 식별자는 흔히 4가지 방법으로 생성된다.

  • 특정 규칙에 따라 생성
  • UUID 나 Nano ID 같은 고유 식별자 생성기 사용
  • 값을 직접 입력
  • 일려번호 사용 (시퀀스나 DB의 자동 증가 컬럼 사용)

2) 밸류

(1) 밸류 타입은 개념적으로 완전한 하나를 표현할 때 사용한다.

예를 들어 ShippingInfo 에 수령인의 정보가 필요하기에 receiverName, receiverPhoneNumber 가 String 값으로 존재한다면 이를 하나의 밸류 타입으로 만들 수 있다.

public class Receiver {
	private String name;
	private String phoneNumber;
	...
}

이처럼 완전히 같은 개념의 데이터를 하나로 표현할 수 있다.

(2) 밸류 타입은 하나의 데이터만 표현할 수도 있다.

OrderLine 이 가격, 개수, 총 가격을 데이터로 가지고 있을 때 이들의 타입은 int 이다. 하지만 가격과 총 가격은 “돈”을 의미하는 새로운 밸류 타입을 만들어 표현할 수 있다.

public class Money{
	private int value;

	...
}

이를 통해 가격이라는 특성을 더 잘 나타낼 수 있으며 Money 안에 로직을 추가함으로써 단순 정수 계산이 아닌 돈 계산이라는 의미를 부여할 수 있다.

(3) 밸류 객체는 불변 객체이다.

밸류 객체는 데이터를 변경할 때 기존 데이터를 변경하지 않고 새로운 밸류 객체를 생성하는 방식을 선호한다.

불변 객체를 선호하는 이유는 명확하다. 이 부분에 대해서는 좋은 글이 많으니 다른 글을 참고하도록 하자.

결국 밸류 타입은 데이터의 안정성을 높인다.

6. 개발과 협업

결국 개발자는 각 도메인에서 필요로하는 기능을 개발해야한다. 이는 각 도메인 전문가들이 개발자에게 기능 개발을 요구하는 것으로 비롯된다.

즉, 개발자는 요구사항을 올바르게 이해하는 것이 중요하다. 책에서는 개발자와 도메인 전문가의 소통을 강조하며, 이해관계자와 개발자도 도메인 지식을 갖출 것을 요구한다.

추가적으로 도메인 개발자들이 요구하는 사항이 항상 올바른 것이 아닐 수 있기에 개발자는 대화를 통해 진짜 원하는 것이 무엇인지 파악하는 것이 중요하다.

(1) 유비쿼터스 언어

개발에서 네이밍의 중요성은 어디에서나 강조된다. 도메인에서 사용되는 용어를 코드에 반영하지 않으면, 그 코드는 개발자에게 코드의 의미를 또 해석해야하는 부담을 준다. 결국 네이밍을 잘 하자는 뜻이다.

에릭 에반스는 도메인 주도 설계에서 언어의 중요성을 강조하기 위해 유비쿼터스 언어라는 용어를 사용하였다. 도메인을 개발하기 위해서는 도메인 전문가와 개발자 등이 공통의 언어를 만들어 같은 용어를 사용해야한다. 이를 통해 개발자는 도메인과 코드 사이에서 불필요한 해석 과정을 줄일 수 있다.

업데이트:

댓글남기기