고딩왕 코범석

1장 도메인 본문

Book Lounge/도메인 주도 개발 시작하기

1장 도메인

고딩왕 코범석 2022. 7. 1. 22:53
반응형

1장 - 도메인

Index

  1. 도메인 모델이란?
  2. 도메인 모델 패턴
  3. 도메인 모델 도출
  4. Entity & Value

도메인 모델이란?

  • 특정 도메인을 개념적으로 표현한 것

하위 도메인과 모델

  • 도메인은 다수의 하위 도메인으로 구성된다.
  • 만약 용어가 같더라도 각 도메인마다 용어의 의미는 달라진다.
    • 예를 들어, 카탈로그 도메인의 상품은 상품의 상세 정보들을 다룬다.
    • 하지만 배송 도메인의 상품은 배송되는 상품에 대한 상세 정보들을 다룬다.
  • 즉, 특정 도메인마다 하위 도메인 모델을 공유하지 않고 따로 만들어야 한다.
    • 카탈로그 도메인의 상품과 배송 도메인의 상품을 따로 만들어야 한다.

도메인 모델 패턴

영역 설명
UI or Presentation 사용자의 요청을 처리하고 사용자에게 정보를 보여주는 책임을 갖는 영역. 소프트웨어를 사용하는 사람 뿐만아니라 외부 시스템일 수 있다.
Application 사용자가 요청한 기능을 실행하는 영역. 도메인 계층을 직접 조합하여 기능을 실행한다.
Domain 시스템이 제공할 도메인의 규칙을 구현하는 영역
Infrastructure 데이터베이스, 메시징 시스템과 같은 외부 시스템과의 연동을 처리한다.

Domain Layer (Domain Model Pattern)

  • 도메인의 핵심 규칙을 구현한다.
    • 주문 도메인의 경우 ‘출고 전에 배송지를 변경할 수 있다.’, ‘주문 취소는 배송 전에만 할 수 있다.’를 구현한 코드가 도메인 레이어에 위치한다.
  • 이런 도메인 규칙을 객체 지향 기법으로 구현하는 패턴이 도메인 모델 패턴이다.
  • 주문 도메인에서 주문 상태에 따라 주문 취소 가능 여부에 대해 구현해본다면 다음과 같이 구현할 수 있다.
public enum OrderState {
    PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED, CANCELED
}

public class Order {

    private ShippingInfo shippingInfo;
    private OrderState orderState;

    public void changeShippingInfo(ShippingInfo newShippingInfo) {
        if (!isShippingChangeable())
            throw new IllegalStateException();
    }

    private boolean isShippingChangeable() {
        // logic...
    }
}

중요한 점은 배송지 변경에 대해 판단하는 로직은 도메인 모델인 OrderOrderState에서 구현한다는 점이다. 이런 코드가 도메인 모델에만 위치하기 때문에 규칙 변경, 확장 시 다른 코드에 대한 영향을 덜 줄 수 있다는 장점이 있다.

도메인 모델 도출

모델을 구성하는 핵심 구성 요소, 규칙, 기능을 먼저 찾기

주문 항목이 어떤 데이터로 구성되는지 알려주는 요구사항

  • 한 상품을 한 개 이상 주문할 수 있다.
  • 각 상품의 구매 가격 합은 상품 가격에 구매 갯수를 곱한 값이다.
  • 배송지 정보는 받는 사람 이름, 전화번호, 주소로 구성된다.

기능에 대한 요구사항

  • 출고를 하면 배송지를 변경할 수 없다.
  • 출고 전에 주문을 취소할 수 있다.
  • 고객이 결제를 완료하기 전 까지는 상품을 준비하지 않는다.

주문과 주문 상품에 대한 관계를 알려주는 요구 사항

  • 최소 한 종류 이상의 상품을 주문해야 한다.
  • 총 주문 금액은 각 상품의 구매 가격 합을 모두 더한 금액이다.

조건에 대한 요구 사항

  • 주문할 때 배송지 정보를 반드시 지정해야 한다.
  • 최소 한 종류 이상의 상품을 주문해야 한다.
  • 출고를 하면 배송지를 변경할 수 없다.
  • 출고 전에 주문을 취소할 수 있다.
  • 고객이 결제를 완료하기 전 까지는 상품을 준비하지 않는다.

Entity & Value

엔티티와 밸류를 구분해야 도메인을 올바르게 설계하고 구현할 수 있다. 그 전에 이 둘의 명확한 차이를 알아야 한다.

Entity

  • 고유한 식별자를 가지고 있다.
  • 이 식별자는 절대 변경되지 않는다.
  • 주문 도메인을 예를 들면 주문 번호가 있다.
    • 주문 도메인을 Order 엔티티로 도출하고 해당 엔티티에 고유한 ID를 부여한다.
  • 엔티티 객체에서 식별자가 같으면 같은 객체로 판단한다. 따라서 equals(), hashCode()를 재정의한다.

Value

  • 밸류 타입은 완전한 하나를 표현할 때 사용한다.
  • 꼭 두개 이상의 필드를 가져야하는 것은 아니며 의미를 명확하게 표현하기 위해 사용한다.
  • 밸류 타입의 장점은 밸류 타입을 위한 기능을 추가할 수 있다는 점이다.
    • 예를 들어 받는 사람 이라는 개념을 밸류 타입으로 표현하면 다음과 같은 밸류 타입 클래스를 만들 수 있다.
    public class Receiver {
        private String name;
        private String phoneNumber;
    }
    • 또한, 주소 라는 개념을 밸류 타입으로 표현하면 다음과 같다.
    public class Address {
        private String address1;
        private String address2;
        private String zipCode;
    }
  • 밸류 타입의 객체를 변경 시 기존 데이터를 변경하기 보다는 변경한 데이터를 갖는 새로운 밸류 객체를 생성하여 이를 저장하는 방식이 선호된다.
  • 이처럼, 데이터 변경 기능을 제공하지 않는 타입을 불변이라고 한다. 불변의 장점은 보다 안전한 코드를 작성하는데 있다.
    • 안전한 코드?
    • 아래 상황의 경우 price와 OrderLine의 price가 모두 변경되는 대참사가 일어난다.
    Money price = new Money(1000);
    
    OrderLine orderLine = new OrderLine(product, price, 2);
    
    price.setValue(2000);  // price와 orderLine.getPrice 값 모두 변경
  • 밸류 객체를 비교할 때는 밸류 객체의 필드들이 모두 같은지 비교한다.

Entity 식별자를 Value Type으로 변경

  • 식별자는 문자열일 경우 하나의 Value Type을 정의하여 명확하게 설계할 수 있다.
public class Order {

    private OrderNo id;

    public OrderNo getOrderNo() {
        return id;
    }
}
 
반응형