Domain driven design

DDD Start #1 도메인 모델 시작 키워드 요약

HaningYa 2021. 10. 10. 00:51
728x90
  • 도메인: 소프트웨어로 해결하고자 하는 문제 영역
  • 상위 도메인: 온라인 서점
  • 하위 도메인: 회원, 주문, 혜택, 결제, 리뷰, 정산 ..etc
  • 하위 도메인 중 일부는 외부 시스템을 이용할 수 있다.
  • 도메인 모델: 특정 도메인을 개념적으로 표현한것
  • 객체 기반 주문 도메인 모델: 기능과 주요 데이터 구성을 파악하는데 적합함
  • 상태 다이어그램을 이용한 주문 상태 모델링, 그래프, 수학공식 등으로 표현할 수 있음
  • 도메인 모델은 도메인 자체를 이해하기 위한 개념 모델
  • 개념 모델구현모델은 다르지만 구현모델이 개념모델을 따라가도록 구현가능
    ex) 객체 기반 개념 모델 -> 객체 지향 언어 사용, 수학적 모델 -> 함수형 언어 사용
  • 하위 도메인을 하나의 다이어그램에 모델링 하면 안됨 -> 하위 도메인에 따라 용어의 의미가 달라지기 때문!
  • 도메인 모델 패턴
    • 일반적인 아키텍쳐 구성
      • UI 또는 Presentation 계층: 사용자의 요청을 처리하고 사용자에게 정보를 보여줌
      • Application 계층: 사용자가 요청한 기능 수행, 업무 로직을 직접 구현하지 않고, 도메인 계층을 조합해서 기능을 실행
      • Domain 계층: 시스템이 제공할 도메인의 규칙을 구현
      • Infrastructure 계층: 디비나 메세징 시스템과 같은 외부 시스템과의 연동 처리
    • 위 레이어에서 도메인 계층은 도메인의 핵심 규칙을 구현
      ex) 출고전 배송지를 변경할 수 있다. 주문 취소는 배송 전에만 할 수 있다.
    • 이런 도메인 핵심 규칙을 "객체 지향 기법"으로 구현하는 패턴이 도메인 모델 패턴
    • 왜 중요? -> 핵심 규칙을 구현한 코드는 도메인 모델에만 위치하기 때문에 규칙이 바뀌거나 확장될 때 다른 코드에 영향을 덜 주고 변경 내역을 모델에 반영할 수 있음
  • 도메인 모델 도출과정 생략
  • 엔티티
    • 엔티티 객체마다 고유한 식별자(Idenfitier) 를 가짐 (주문 번호)
    • 엔티티의 식별자는 바뀌지 않음 (주문에서 배송지 주소나 배송상태가 바뀌어도 주문번호는 바뀌지 않음)
    • 두 엔티티 객체의 식별자가 같으면 두 엔티티는 같다고 판단
    • 식별자 생성방법
      • 특정 규칙에 따라 생성 (현재시간 + 다른값)
      • UUID 사용
      • 값을 직접 입력
      • 일련번호 사용(시퀸스나 DB auto increment)
  • 레포지토리: 도메인 객체를 디비에 저장할 때 사용하는 구성요소 (4장에서 나옴)
  • 밸류타입: 개념적으로 완전한 하나를 표현할 때 사용
public class ShippingInfo {
    // 아래 두 변수는 "받는 사람" 의 개념으로써 동일하다. (하나의 개념을 포함한다.)
    private String receiverName;
    private String receiverPhoneNumber;
    
    //  아래 두 변수는 "주소" 라는 개념으로써 동일하다. (하나의 개념을 포함한다.)
    private String shippingAddress1;
    private String shippingAddress2;
    private String shippingZipcode;
}

위 예제에서 개념적으로 완전한 하나를 표현하기 위해 Receiver 밸류 타입을 만들 수 있다.

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

    public Receiver(String name, String phoneNumber) {
        this.name = name;
        this.phoneNumber = phoneNumber;
    }

    public String getName() {
        return name;
    }

    public String getPhoneNuber() {
        return phoneNumber;
    }
}

밸류 타입을 사용함으로써 개념적으로 완전한 하나를 표현할 수 있다.

  • 밸류타입이 꼭 두개 이상의 데이터를 가질 필요는 없다. 
    예를들어 private int price가 돈이라는 의미를 명확하게 하기 위해
    Money타입을 만들어 private Money price라고 표현할 수 있다.
    또한 Money 타입에 돈계산을 위한 기능도 추가할 수 있다.
  • 불변타입: 값을 변경할 수 있는 기능을 제공하지 않는 타입
  • 밸류는 불변타입으로 구현한다.
    - 안전한 코드 작성 가능-> 참조 투명성, 스레드에 안전함
  • 엔티티 비교는 identifier로 했지만 밸류 비교는 모든 속성이 같은지 비교
  • 식별자를 위한 밸류 타입을 사용가능
    예시) 기존 주문번호를 private int id; 로 선언했다면 OrderNo 밸류타입을 추가하여 private OrderNo id; 로 선언함으로서 해당 필드가 주문번호라는걸 알 수 있음
  • 도메인 모델에 set 메서드 넣지 마라
    -> set 은 도메인의 핵심 개념이나 의도를 코드에서 사라지게함
    예시) changeShippingInfo() -> setShippingInfo() 애써 이해한 도메인 지식이 코드에서 사라지게됨
    -> 도메인 객체를 생성할 때 완전한 상태가 아닐 수 있음
    예시) init에서 값을 전달하는게 아닌 set으로 객체 값을 전달하면 값이 누락될 수 있음 -> 생성자에서 필요한 데이터 다 받도록 수정

    * private set 함수는 외부에서 데이터를 변경할 목적으로 사용X하기 때문에 써도됨
  • 불변 밸류 타입을 사용하려면 자연스럽게 set 메서드는 구현하지 않음
  • DTO: Data Transfer Object -> Presentation layer과 domain layer 사이 데이터 주고받을 때 사용
  • 도메인 용어
public OrderState1 { 
    STEP1, STEP2, STEP3, STEP4 ...
}

public OrderState2 { 
    PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING ...
}

OrderState1은 결제 대기중, 상품 준비중, 출고 완료됨 등등의 도메인 용어가 코드에 반영되있지 않음
OrderState2 처럼 코드에서 중요 규칙이 드러나야함


엔티티

  • 고유의 식별자를 갖는 객체
  • 자신의 라이프사이클을 가짐
  • 주문, 회원, 상품과 같이 도메인의 고유한 개념을 표현
  • 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공

밸류

  • 고유의 식별자를 갖지 않는 객체 
  • 주로 개념으로 하나인 도메인 객체의 속성을 표현할 때 사용
  • 배송지 주소를 표현하기 위한 주소(Address), 구매 금액을 위한 금액(Money)와 같은 타입
  • 엔티티의 속성으로 사용될 뿐만 아니라 다른 밸류 타입의 속성으로도 사용될 수 있음

에그리거트

  • 관련된 엔티티와 벨류 객체를 개념적으로 하나로 묶은것
  • 주문과 관련된 Order 엔티티, OrderLine 밸류, Orderer밸류 객체를 '주문' 에그리거트로 묶을 수 있음

리포지토리

  • 도메인 모델의 영속성 처리
  • DBMS 테이블에서 엔티티 객체를 로딩하거나 저장하는 기능을 제공

도메인 서비스

  • 특정 엔티티에 속하지 않은 도메인 로직을 제공
  • '할인 금액 계산'은 상품, 쿠폰, 회원등급, 구매금액 등 다양한 조건을 이용하는데
    이렇게 여러개의 엔티티와 밸류를 필요로 할 경우 도메인 서비스에서 로직을 구현

생각

  • 밸류 타입의 목적이 가독성을 위해 "변수 네이밍" + "변수 타입" 둘다 활용하는 느낌인데 맞는건가
  • 엔티티와 밸류를 단순히 class와 내부 프로퍼티로 구분할 수 없는것 같은데 class도 엔티티 또는 밸류가 될수 있고 프로퍼티는 엔티티가 될 수 없고 프로퍼티는 밸류가 될 수 있는건가..?
  • 예시) 기존 주문번호를 private int id; 로 선언했다면 OrderNo 밸류타입을 추가하여 private OrderNo id; 로 선언함으로서 해당 필드가 주문번호라는걸 알 수 있음 -> 그냥 변수명을 orderNo로 하면 안되나? 굳이 타입관련한 함수가 없다면?
  • 도메인이 소프트웨어로 해결하고자 하는 문제영역이면 도메인 지식은 소프트웨어 요구사항, 도메인 핵심 규칙은 소프트웨어 비즈니스 로직이라고 볼 수 있는건가
  • 그럼 결국 도메인 주도 개발이라는게 요구사항에 따라 개발을 진행하는 방법론인건가? 근데 원래 개발할때 요구사항에 따라서 하는거 아닌가 이미 모두가 DDD를 하고 있는건가 뭐지 
  • 아님 TDD처럼 테스트를 먼저 작성하고 개발하는 뜻과 같이 DDD도 도메인 개념 모델을 먼저 작성하고 개발을 해서 DDD인건가?
728x90