코딩못하는사람

1.도메인 모델 시작하기 본문

DDD/도메인 주도 개발 시작하기

1.도메인 모델 시작하기

공부절대안함 2022. 8. 29. 20:03

1.1 도메인이란?

온라인 서점을 개발자에 입장에서 바라보게 되면 온라인 서점은 우리가 소프트웨어를 통해 구현해야 할 대상이 된다.

책 한권을 판매하기 위해 필요한 상품 조회, 구매, 결제, 배송 추적등의 기능을 제공해야 하는데, 이렇게 해결하고자 하는 문제 영역을 도메인(Domain)이라고 한다.

https://incheol-jung.gitbook.io/docs/study/ddd-start/1

한 도메인은 다시 하위 여러 도메인으로 나뉜다. 주문의 하위 도메인은 고객의 주문을 처리하고, 혜택의 하위 도메인은 쿠폰과 할인같은 서비스를 제공할 것이고, 배송 하위 도메인은 구매한 상품을 전달하는 일련의 과정을 처리할 것이다.

도메인마다 고정된 하위 도메인이 존재하는 것은 아니다. 또한, 하위 도메인을 어떻게 구성할지 여부는 상황에 따라 달라진다.

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

https://songii00.github.io/2020/06/14/DDDStart!_Item_1/

 

하위 도메인을 어떻게 구성할지는 필요한 기능에 따라서 달라지게 된다.

 

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

개발자와 도메인 전문가 사이의 협업에서 요구사항을 올바르게 이해하는 것이 중요하다.

요구사항을 올바르게 이해하려면 개발자와 도메인 전문가가 직접 대화하는 것이 가장 쉽다. 사이에 전달자가 많을수록 정보의 왜곡과 손실이 생기게 되고 변질이 일어나게 된다.

더 나아가 원하는 제품을 만들기 위해 개발자도 도메인 지식을 갖추는 것이 중요하다.

 

"Garbage in, Garbage out" - 잘못된 값이 들어가면 잘못된 결과가 나온다.

개발자와 도메인 전문가가 직접 소통할수록 요구사항 변질이 줄지만, 항상 올바른 요구사항을 주는것은 아니다.

도메인 전문가가 소프트웨어를 기준으로 요구사항을 맞출때가 있기 때문에 (ex admin 페이지 등) 사전 협의에서 왜 이런기능을 요구하는지 실제로 원하는게 무엇인지 논의를 통해 진짜로 원하는 것을 찾아가는것이 중요하다.

 

1.3 도메인 모델

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

도메인 모델을 사용하면 여러 관계자들이 동일한 모습으로 도메인을 이해하고 도메인 지식을 공유하는데 도움이 된다.

 

위 사진은 객체를 이용한 도메인 모델이다. 도메인을 이해할때 제공하는 기능과 주요 데이터들을 함께 보면 이해가 쉬운데, 객체 모델은 두가지를 같이 제공해준다는 장점을 가지고 있다.

하지만 객체로만 도메인 모델링이 가능한 것은 아니다.

관계가 중요한 도메인이라면 그래프를, 계산 규칙이 중요하다면 수학공식을 활용해서 도메인을 만들 수 있다.

중요한 것은 도메인을 이해하는데 도움이 되는 방식으로 모델링하는 것이다.

 

도메인 모델은 도메인 이해를 위한 '개념 모델'이지만, 바로 코드를 작성할 수 있는 '구현 모델'은 아니다.

객체 기반 모델을 사용했다면 구현 모델을 객체지향 언어를 이용해 개념모델에 가깝게 구현할 수 있을 것이다.

 

도메인은 다수의 하위 도메인으로 구성된다. 각 하위 도메인마다 다루는 영역이 다르기 때문에 같은 용어라도 의미가 달라질 수 있다. 판매 '상품'의 상품과 배송'상품'의 상품은 각 도메인에서 필요로 하는 저옵가 다르게 될것이다.

따라서 여러 하위 도메인을 하나의 다이어그램에 모델링하면 도메인을 제대로 이해하는데 방해가 될 수 있다.

각 하위 모데인마다 별도로 모델을 만들도록 하자.

 

 

1.4 도메인 모델 패턴

일반적인 애플리케이션 아키텍처

표현: 사용자의 요청을 처리하고 정보를 보여준다.

응용: 사용자가 요청한 기능을 실행한다. 업무로직을 구현하지 않고 도메인 계층을 조합해서 기능 실행

도메인: 시스템이 제공할 도메인 규칙을 구현

인프라스트럭처: DB나 메시징 시스템과 같은 외부 시스템과의 연동을 처리

 

도메인 모델은 보통 두가지 뜻을 가지는데

1.도메인 자체를 이해하는데 필요한 개념모델 

2.도메인 모델 패턴 (아키텍쳐 중 도메인 계층을 객체지향기법으로 구현하는 패턴)

 

코드 예시로 Order와 OrderState를 가지고 배송지 변경이 가능한 상태인지에 대한 메서드를 구현한다.

OrderState의 상태만 가지고 판단을 할 수 있다면 OrderState에서, Order 클래스의 다른 정보까지 필요하다면 Order에서 판단하도록 구현한 코드를 예시로 든다.

주용한 점은, 주문과 관련된 중요 로직을 도메인 모델인 order, orderstate에서 구현한다는 점이다. '핵심 로직을 구현한 코드는 도메인 모델에만 위치하기 때문에 규칙이 바뀌거나 확장해야 할 때 다른 코드에 영향을 줄일 수 있다.

 

개념모델은 순수하게 문제를 분석한 결과물. DB,트랜잭션 처리, 성능, 구현 기술과 같은 것을 고려하고 있지 않음.

프로젝트 처음부터 완벽한 도메인을 표현하는 모델을 시도할 수 있지만 쉽지 않고, 도메인 지식이 늘어나면서 보완하거나 변경할일이 생긴다. 따라서 완벽한 개념 모델을 만들기보다 개요를 알 수 있는 수준으로 개념모델을 작성해 윤곽을 잡고, 구현하는 과정에서 개념모델 to 구현모델로의 점진적 발전 필요.

 

1.5 도메인 모델 도출

도메인 모델을 점진적으로 만들어나가는 과정 예시

 

40p 점점 구체화 되는 메서드명 

 

문서화는 지식의 공유를 위함이다. 실제 소프트웨어를 분석하며 코드를 모두 보는것 보다 전반적인 기능 목록이나 모듈 구조, 빌드 과정은 정리한 문서를 참조하는 것이 전반을 이해하는데 도움이 된다. 전체 구조에서 더 깊게 이해가 필요한 부분을 코드로 분석하면 된다.

도메인 지식이 잘 묻어나도록 코드를 작성하지 않으면 코드의 동작과정은 이해해도 도메인 관점에서 이해하는데 힘들 수 있다. 단순히 보기 좋은 코드가 아닌 도메인을 잘 표현하는 코드를 짜야한다.

1.6 엔티티와 벨류

요구사항에서 도출한 도메인은 크게 엔티티와 밸류로 구분.

 

엔티티와 벨류를 제대로 구분해야 올바르게 설계가 가능하기 때문에 차이를 명확하게 이해해야 한다.

 

엔티티

엔티티의 가장 큰 특징은 식별자를 가진다(고유하고 변하지 않는).

ex) 주문(order) 도메인의 주문. 주문번호(orderNumber)를 가지고 있는데, 각 주문 마다 서로 다름

 

엔티티의 식별자 생성시점

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

 

자동증가 칼럼은 DB테이블에 데이터를 삽입해야 비로소 값을 알 수 있다(엔티티를 생성할 때 식별자를 전달 불가)

 

밸류타입

밸류타입의 장점이 많구나.

ReceiverName필드와 receiverPhoneNumbe 필드는 서로 다른 데이터를 담고있지만, 두필드는 개념적으로 받은 사람을 즉 실제로 하나의 개념을 표현하고 있다(주소1, 주소2, 주소3)
밸류타입은 개념적으로 완전한 하나를 표현할때 사용한다.
 
ex)address1, address2, zipcode를 클래스의 필드로 사용하는 것 보다, Address라는 밸류타입을 사용하게 되면 코드의 의미를 보다 명확하게 표현할 수 있다.
밸류 타입은 불변으로 구현하는데 가장 큰 이유는 안전한 코드이다.
why? 불변 객체가 아니여서 일부 필드가 set메서드로 잘못반영된다면 이상이 발생하게 된다.
+ 밸류 객체를 비교할때는 모든 필드를 확인하자.
 
엔티티식별자의 실제 데이터는 String과 같은 문자열로 많이 쓰는데, 도메인에서 특별한 의미를 가지는 경우가 많기 때문에 벨류 타입을 사용해서 의미가 잘 드러나게 사용할수도있다.
주문이라는 도메인에서 Order의 식별자를 String 대신 OrderNo 밸류 타입으로 사용한다면 코드의 의미가 명확해진다.
 
도메인 모델에 set 메서드를 넣지 않기. -그냥 사용하지 말라고만했었음-
set 메서드는 도메인의 핵심 개념이나 의도를 코드에서 사라지게 한다." 이게 무슨의미일까"
changeShippingInfo()가 배송지 변경의 의미라면 setShippingInfo()는 단순한 배송지값 설정이다.
completePayment()가 결제 완료라면, setOrderState는 단순히 주문 상태값 변경이다.
completePayment로 결제완료를 구현하는 것이 도메인 지식을 코드로 구현하는데 자연스럽다.
습관적으로 set을 통해 상태를 변경하는 것은 도메인 지식이 코드에서 사라지게 된다.
 
추가적으로 도메인 객체를 생성할 때 온전하지 않는 상태가 될 수 있다.
set 메서드로 필요한 모든 값을 전달하다보면 도메인 객체가 불완전한 상태로 사용될 수 있다.
생성자를 통해 필요한 데이터를 모두 받는것이 좋고, 생성자를 호출하는 시점에 데이터를 검사까지 가능하다.
클래스 내부 데이터를 변경하기 위한 set은 private를 사용해서 외부의 사용을 막는다.
특별한 이유가 없다면 set 메서드를 구현할 필요가 없는 불변 밸류 타입을 사용하자.
 
++DTO에는 get/set 메서드를 구현하기도 하지만, 프레임워크에서 set메서드가 아닌 private에 직접 값을 할당하는 기능을 제공하고 있어 set메서드 없이 불변객체의 장점을 DTO에 까지 확장할 수 있으니 사용하도록 하자.
 
 
1.7 유비쿼터스언어
코드를 작성할 때 도메인에서 사용하는 용어는 매우 중요하다.
도메인에서 사용하는 용어를 코드에 반영하지 않으면 그 코드는 개발자에게 코드의 의미를 해석해야 하는 부담을 준다
enum STEP1, STEP2, STEP3이 아닌 PAYMENT_WAITING, PREPARING, SHIPPED 와 같이 (호불호가 있는듯 함)
알맞은 영어 단어를 찾는 것은 쉽지 않지만 노력하자. 노력하지 않는다면 코드는 도메인과 점점 멀어지게 된다.

 

'DDD > 도메인 주도 개발 시작하기' 카테고리의 다른 글

9.도메인 모델과 바운디드 컨텍스트  (0) 2022.10.27
2.아키텍처 개요  (0) 2022.09.03
Comments