객체지향

[DDD, 도메인 주도 개발] 1. 도메인 모델 시작하기

마디니 2023. 3. 6. 23:10
반응형

도메인이란?

도메인이란 소프트웨어의 구현을 통해 해결하고자 하는 문제나 주제를 말한다.
온라인 서점을 이용할 때 우리는 책을 검색하고, 장바구니에 담거나 쿠폰이 있는지 찾아보거나 구매를 한다.

구매를 할 때에는 다양한 페이나 포인트를 결제에 사용한다. 결제 이후에는 배송 추적이나 주문 상세 내역을 확인한다.

이때 '온라인 서점'이 하나의 도메인이 될 수 있다.

 

한 도메인은 하위 도메인으로 나눌 수 있는데, 예를 들어 온라인 서점의 하위 도메인으로는

  • 주문 : 고객이 구매하는 과정을 처리
  • 혜택: 할인이나 포인트 사용 등
  • ...

하위 도메인을 어떻게 구성할 지는 상황에 따라 다를 수 있다.

특정 도메인을 위한 소프트 웨어라고 해서 도메인의 모든 기능을 구현할 필요는 없다.

배송이나 결제는 외부 시스템을 사용하여 필요한 기능만 일부 연동한다.

 

도메인 지식공유

다양한 도메인에는 각 분야별 전문가들이 있고 이들은 본인들이 원하는 기능 개발을 요구 한다.

개발자는 이들의 요구사항을 분석하고 설계하여 코드를 작성하고 테스트, 배포한다.

요구사항을 제대로 이해하고 이를 반영하는 것이 중요한데, 

요구사항을 제대로 이해하지 못하고 만든 소프트웨어는

쓸모없어지거나

변경하거나 다시만들어야 하는 부분이 많아지고

일정이 밀리거나 프로젝트 실패로까지 이어질 수 있다.

 

어떻게 요구사항을 올바르게 이해할 수 있을까?

개발자와 전문가가 직접 대화 하는 가장 간단한 방법이 있다.

개발자와 전문가 사이 전달자가 많아지면 정보가 왜곡될 확률이 높아진다.

그래서 중간에 존재하는 이해관계자와 개발자들 역시 도메인지식을 갖추는 것이 중요하다.

 

Garbage in, Garbage out

전문가가 요구한 내용이 항상 올바르지는 않다. 혹은 그들이 원하는걸 정확하게 표현하지 못할 때도 있다.
그래서 개발자는 왜 이런 기능을 요구하는지, 실제로 원하는게 무엇인지 대화를 통해 진짜 요구사항을 찾아야 한다.

 

도메인 모델

도메인 모델이란, 특정 도메인을 개념적으로 표현한 것으로 기본적으로 도메인 자체를 이해하기 위한 개념 모델이다. 도메인을 이해하는데 도움이 된다면 형태나 표현방식이 중요하지는 않다. 

예)

- 객체 기반 주문 도메인 모델 : 도메인을 이해하려면 도메인이 제공하는 기능과 주요 데이터 구성을 파악해야 하는데, 이때에는 기능과 데이터를 보여주는 객체 모델이 좋다. 

 

https://www.differencebetween.com/difference-between-class-diagram-and-object-diagram/

- 상태변경에 따른 기능들이 많다면 상태 다이어그램도 좋다.

https://www.lloydatkinson.net/posts/2022/modelling-workflows-with-finite-state-machines-in-dotnet/

 

하위모델과 도메인
각 하위 모델들은 다루는 영역이 서로 다르기 때문에 같은 용어라도 그 의미가 달라질 수 있다. (예를 들어 카탈로그 도메인의 상품은 가격이나 상세를 담고 있는 반면, 배송 도메인의 상품은 고객에게 배송되는 물리적인 상품을 의미한다.)
따라서 하위 도메인별로 별도 모델이 필요하다.

 

도메인 모델 패턴

일반적인 애플리케이션의 다키텍처는 아래과 같다.

  • 사용자 인터페이스 (UI) 또는 표현 (Presentation) : 사용자(외부 시스템)의 요청을 처리, 사용자에게 응답 제공
  • 응용(Application)  :도메인 계층을 조합해서 사용자가 요청한 기능을 실행한다.
  • 도메인 : 도메인 관련 로직/규칙을 구현한다. 이 때 객체지향 기법 으로 구현하는 패턴이 도메인 모델 패턴이다.
  • 인프라스트럭쳐(Infrastructure): 외부시스템과의 연동 영역 (데이터 베이스, 메세지큐 등)

객체지향 기법

주문 도메인에 상품 준비중이면 배송지를 변경할 수 있다는 도메인 규칙이 있다고 가정 하자.

이 때 주문과 관련된 중요 규칙을 주문 도메인 모델에서 구현한다. 

Public class Order{
	private OrderState state;
    private ShippingINfo shippingInfo;
    
    public void changeShippingInfo(ShippingInfo info) {
    	if(!isShippingChangeable()){
        	throw new Exception();
        }
        this.shippingInfo = info;
    }
    
    //상품 준비중인지
    private boolean isShippingChangeable() {
    	return state == OrderState.PREPAARING;
    }
}

배송지 변경이 가능한지 판단하는 로직과 배송지에 대한 변경이 도메인 내부에 존재 하도록 했다.

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

개념모델과 구현모델
· 개념모델 : 도메인을 이해하고 관계자들의 획일화된 이해를 위한 목적으로 전체 윤곽을 잡는 용으로 사용
· 구현모델 : 구현이 가능한 형태를 고려한 모델 (데이터베이스, 트랜잭션 처리, 성능 등의 구현 기술 고려)

 

도메인 모델 도출

  1. 기획서, 유스케이스, 사용자 스토리로 요구사항을 분석하고, 관련자와의 대화를 통해 도메인을 이해한다.
  2. 이를 바탕으로 도메인 모델 초안을 만들어야 비로소 코드를 작성할 수 있다.
  3. 요구사항을 기반으로 핵심 구성요소/ 규칙(정책) / 기능 을 찾아 정리한다.

( 도메인 모델 도출 예 )

- 출고 전에 주문을 취소 할 수 있다. ==> 출고 상태로 변경하기, 주문 취소하기와 같은 기능들을 추출해 메소드로 만들 수 있다. 다양한 주문 상태(열거타입)이 필요하다.

- 각 상품의 구매 가격 합은 상품 가격에 구매 개수를 곱한 값이다. ==> 주문 항목은 적어도 주문할 상품 구매 개수와 가격을 포함하고 있어야 한다.

- 최소 한 종류 이상의 상품을 주문해야 한다. ==>  주문은 최소 한개 이상의 주문 항목을 포함 해야 한다는 규칙이 있어야 한다. Order를 생성할때 OrderLine 의 리스트를 생성자에 넘겨준다.

※ 요구사항을 분석하면서 더 깊이 있에 도메인에 대해 이해하게 되고 최초 이해하고 정의 했던 용어가 바뀔 수 도 있다.

문서화, 코드 자체로 문서화가 될 수 없을까?
코드 자체도 문서가 될 수 있다. 따라서 도메인의 흐름대로 잘 해석되도록, 도메인을 잘 표현하는 코드를 만드는 것이 중요하다.
하지만 코드는 구현 상세를 모두 기술 하고 있기 때문에, 도메인이나 소프트웨어를 전반적으로 이해하기에는 시간이 많이 든다.
따라서 이를 상위 수준에서 정리한 문서를 참조하는 것이 전반적인 내용을 빠르게 이해하기에는 좋다.

 

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

Entity

  • 도메인 모델 내에서 식별자를 갖는 객체로, 식별자는 엔티티를 생성하고, 속성을 바꾸고, 삭제할때까지 라이프 사이클을 함께 한다.
  • 식별자는 엔티티마다 고유하다 -> 두 엔티티 객체의 식별자가 같으면 두 엔티티는 같다.
  • 엔티티와 식별자의 예 - 주문 번호, 회원 아이디, 회원 이메일 등

[엔티티 식별자의 생성]
엔티티 식별자를 생성하는 시점은 도메인 특징이나 사용 기술에 따라 달라진다.

  • 특정 규칙에 따라 생성
  • UUID나 Nano ID 와 같은 고유 식별자 생성기 사용
  • 값을 직접 입력
  • 일련번호 사용(시퀀스나 DB의 자동 증가 컬럼 사용) - DB자동증가 사용시에는 데이터를 추가 전까지 식별자를 알 수 없다.

Value

  • 개념적으로 완전한 하나를 표현하는 단위
  • 도메인 이해를 돕기 위해 사용되거나 의미를 명확하게 표현하기 위해 사용한다.
  • 불변(immutable)으로 만들어 참조 투명성(외부 요소에 영향을 받지 않음)과 Tread Safe하게 만드는 것이 좋다.
  • 엔티티의 식별자를 밸류타입으로 만들어 사용하기도 한다.
  • 밸류의 예
    • 객체내 주소지번, 우편번호 등의 필드는 Address 라는 타입에 포함시켜 좀 더 완전한 하나의 개념을 표현한다.
    • 주문 가격이나 상품 가격을  int 타입대신 Money 라는 좀더 명확한 의미의 타입을 만들어서 사용한다.
도메인 모델에 set 메서드는 사용하지 않는 것이 좋다.
⊙ set 메소드를 사용하면 생성 시점에 필요한 필드중 하나가 빠져 불완전한 상태로 객체가 사용될 수 있다.
⊙ set보다는 생성시점에 필요한 것들은 모두 생성자를 통해 받는 것이 좋다. -> 생서자를 호출 하는 시점에 validation 체크 가능
⊙ 외부에서 데이터를 변경할 목적으로 사용되는 set과 내부에서 사용하는 private set 메소드는 다름
Thread Safe (쓰레드 세이프)란?
멀티 쓰레드 프로그래밍에서, 어떤 공유 자원에 여러 쓰레드가 동시에 접근해도, 프로그램 실행에 문제가 없는 상태

 

도메인 용어와 유비쿼터스 언어

도메인에서 사용하는 용어를 코드에 반영하지 않으면 코드의 '의미'를 해석해야하 는 부담이 있다.

예) [STATUS1, STATUS2, STATUS3] 보다는  [PAYMENT_WATING, SHIPPED, DELIVERYING]

 

'유비쿼터스 언어'란 ?

도메인 문제를 해결할 때 개발자, 전문가, 관계자 모두가 사용하는 도메인 관련 공통 언어

대화, 문서, 도메인 모델, 코드, 테스트등 모든 곳에서 모두가 같은 용어를 사용한다.

 

- 소통시 오해를 줄일 수 있다.

- 코드 의미 해석에 대한 피로도를 낮춘다.

 

시간이 지나 도메인에 대한 이해도가 높아지면 새롭게 이해한 내용에 대한 용어를 찾아 공유한다 -> 산출물에 최신 용어를 적용한다.

도메인 용어에 알맞은 단어를 찾는 시간을 아까워 하지 말자.

 

Reference

도메인 주도개발 시작하기, 최범균 저

 

※ 책을 읽고 개인적으로 이해한 바를 정리해 놓은 내용입니다. 참고 부탁드립니다.

반응형