본문 바로가기

Dev Book Review/DDD START!

[DDD START!] Chap1. 도메인 모델 시작

도메인

소프트웨어로 해결하고자 하는 문제 영역이다.

예를 들어 '온라인 서점'이라는 대상은 개발자가 구현해야 할 소프트웨어의 대상이 된다. 즉, 도메인이다.

 

하나의 도메인은 여러 개의 하위 도메인으로 다시 나눌 수 있다. 각 하위 도메인은 다른 하위 도메인과 연동하여 완전한 기능을 제공한다. 이러한 하위 도메인을 어떻게 구성해야 하는지는 상황에 따라 달라진다. (같은 이커머스라 하더라도 대상에 따라 제공하는 기능이 다르다)

 

특정 도메인을 위한 소프트웨어라고 해서 도메인이 제공해야 할 모든 기능을 구현하지는 않는다.

예를 들어 배송/결제 시스템은 외부 업체의 시스템을 사용한다.

도메인 모델

객체 기반 주문 도메인 모델

특정 도메인을 개념적으로 표현한 것이다.

도메인 모델이란 기본적으로 도메인 자체를 이해하기 위한 개념 모델이다.

개념 모델을 가지고 바로 코드를 작성할 수 없기 때문에, 구현 기술에 맞는 구현 모델이 따로 필요하다.

일반적으로 구현 모델을 개념 모델을 최대한 따르도록 설계한다.

 

앞서 말했듯 도메인은 다수의 하위 도메인으로 구성된다.

각 하위 도메인이 다루는 영역은 서로 다르기 때문에 같은 용어라도 하위 도메인마다 의미가 달라질 수 있다.

예를 들어 카탈로그 도메인의 상품은 상품 가격, 상세 내용을 담고 있는 정보를 의미한다면

배송 도메인의 상품은 고객에게 실제 배송되는 물리적인 상품을 의미한다.

결론적으로 모델의 각 구성요소는 특정 도메인을 한정할 때 비로소 의미가 완전해지기 때문에, 각 하위 도메인마다 별도로 모델을 만들어야 한다.

도메인 모델 패턴

일반적인 어플리케이션의 아키텍쳐는 아래와 같은 4개의 계층으로 구성된다.

 

  • 표현 : 사용자의 요청을 처리하고 응답
  • 응용 : 도메인 계층을 조합하여 사용자가 요청한 기능을 실행
  • 도메인 : 시스템이 제공할 도메인의 규칙을 구현
  • 인프라스트럭쳐 : 외부 시스템(DB)과의 연동을 처리

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

예를 들어 주문 도메인과 같은 경우 '출고 전에 배송지를 변경할 수 있다' 같은 규칙을 구현한 코드가 도메인 계층에 위치하게 된다.

이러한 도메인 규칙을 객체 지향 기법으로 구현하는 패턴이 도메인 모델 패턴이다.

 

핵심 규칙을 구현한 코드는 도메인 모델에만 위치하기 때문에 규칙이 바뀌거나 규칙을 확장해야 할 때 다른 코드에 영향을 덜 주고 변경 내역을 모델에 반영할 수 있게 된다.

도메인 모델 도출

개발자는 구현하고자 하는 도메인에 대한 이해가 선행되어야 한다.

기획서, 유스 케이스, 사용자 스토리 등을 통해 요구사항의 도메인을 이해한 뒤 도메인 모델 초안을 만들어야 한다.

 

도메인을 모델링할 때 기본이 되는 작업은 요구사항 분석을 통해 모델을 구성하는 핵심 구성요소, 규칙, 기능을 찾는 것이다.

엔티티(Entity)와 밸류(Value)

도출한 모델은 크게 엔티티와 밸류로 나눌 수 있다.

1) 엔티티

가장 큰 특징은 고유한 식별자를 가진다. 이때 식별자는 바뀌지 않는다.

흔히 식별자는 다음 중 한 가지 방식으로 생성한다.

 

- 특정 규칙에 따라 생성

- UUID 사용

- 값을 직접 입력

- 일련번호 사용 (시퀀스나 DB의 자동 증가 컬럼 사용)

2) 밸류 타입 

개념적으로 완전한 하나를 표현할 때 사용한다.

Value Type

밸류 타입이 꼭 두 개 이상의 데이터를 가져야 하는 건 아니다. 의미를 명확하게 표현하기 위해 밸류 타입을 사용하기도 한다. (ex. Money)

이때 장점은 밸류 타입을 위한 기능을 추가할 수 있다는 것이다. 위에서는 돈 계산을 위한 기능을 추가했다.

 

밸류 타입 객체의 데이터를 변경할 땐 기존 데이터를 변경하기보다 변경한 데이터를 가지는 새로운 객체를 생성하는 방식을 선호한다.

이렇게 밸류 타입을 불변으로 구현하게 되면 보다 안전한 코드를 작성할 수 있다. (의도하지 않은 곳에서 데이터가 변경되는 걸 방지)

 

추가적으로 밸류 타입 객체가 같은지 비교할 때는 모든 속성이 같은지 비교해야 한다.

3) 엔티티 식별자와 밸류 타입

일반적으로 식별자는 String 타입이지만, 식별자가 도메인에서 특별한 의미를 가지는 경우 밸류 타입을 사용해서 의미를 전달한다. (ex. OrderNo) 

도메인 모델에 set 메서드 넣지 않기

다음과 같은 이유로 도메인 모델에 setter 메서드를 작성하지 않아야 한다.

1) setter 메서드는 도메인의 핵심 개념이나 의도를 코드에서 사라지게 한다.

단순하게 setShippingInfo()로 선언하기보다 changeShippingInfo()와 같이 의도를 잘 전달할 수 있어야 한다.

 

또한 구현할 때도 completePayment() 메서드에서는 결제 완료와 관련된 처리 코드를 함께 구현하는 게 자연스럽다.

반면에 setOrderState()는 단순히 상태 값만 변경할지 아니면 다른 처리를 함께 해줘야 할지 애매하게 느껴진다.

2) 도메인 객체를 생성할 때 완전한 상태가 아닐 수 있다.

Order order = new Order();

order.setOrderLine(lines);
order.setShippingInfo(shippingInfo);

// 주문자(Orderer)를 설정하지 않은 상태에서 주문 완료 처리
order.setState(OrderState.PREPARING);

 

 

위 코드는 주문자(Orderer)를 설정하는 걸 누락하고 있다.

이러한 상황을 막기 위해 orderer가 null인지 검사하는 코드를 setState() 메서드에 위치하는 것도 부자연스럽다.

 

따라서 생성자를 통해 필요한 데이터를 모두 받아야 한다.

Order order = new Order(orderer, lines, shippingInfo, OrderState.PREPARING);

이제 생성자를 호출하는 시점에 필요한 데이터가 올바른지 검사하면 된다.

 

결론적으로 setter 메서드를 구현해야 할 특별한 이유가 없다면 불변 타입의 장점을 살릴 수 있도록 밸류 타입은 불변으로 구현한다.

도메인 용어

도메인에서 사용하는 용어를 코드에 적절히 반영해야 한다.

이는 코드의 가독성을 높여 코드를 분석하고 이해하는 시간을 절약한다.

'Dev Book Review > DDD START!' 카테고리의 다른 글

[DDD START!] Chap3. 애그리거트  (0) 2021.10.25
[DDD START!] Chap2. 아키텍처 개요  (0) 2021.10.23