kongkong.note

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

DDD

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

hyokong 2025. 11. 16. 23:53

1.1 도메인이란?

한 도메인은 다시 하위 도메인으로 나눌 수 있다.

한 하위 도메인은 다른 하위 도메인과 연동하여 완전한 기능을 제공한다.

 

1.3 도메인 모델

도메인 모델에는 다양한 정의가 존재하는데, 기본적으로 특정 도메인을 개념적으로 표현한 것이다. 기본적으로 도메인 자체를 이해하기 위한 개념 모델이다.

[그림 1.3] 객체 기반 주문 도메인 모델 : 도메인이 제공하는 기능, 주요 데이터 구성 파악에 적합

[그림 1.4] 상태 다이어그램을 이용한 주문 상태 모델링 : 상태 전이 모델링 파악 용이

같은 용어라도 하위 도메인마다 의미가 달라질 수 있다. 도메인에 따라 용어 의미가 결정되므로 여러 하위 도메인을 하나의 다이어그램에 모델링하면 안된다. 각 하위 도메인마다 별도로 모델을 만들어야 한다.

 

1.4 도메인 모델 패턴

애플리케이션에서의 도메인 모델은 아키텍처 상의 도메인 계층(시스템이 제공할 도메인 규칙을 구현)을 객체 지향 기법으로 구현하는 패턴을 말한다.

도메인 계층은 도메인의 핵심 규칙을 구현한다. 이런 도메인 규칙을 객체 지향 기법으로 구현하는 패턴이 도메인 모델 패턴이다.

[주문 도메인의 일부 기능을 도메인 모델 패턴으로 구현 예시]

package com.myshoptest;

import com.myshop.order.command.domain.ShippingInfo;

public class OrderTest {
    private OrderState state;
    private ShippingInfo shippingInfo;
    
    public void changeShippingInfo(ShippingInfo newShippingInfo){
        if(!isShippippingChangeable()){
            throw new IllegalStateException("can't change shipping in " + state);
        }
        this.shippingInfo = newShippingInfo;
    }
    
    private boolean isShippippingChangeable(){
        return state == OrderState.PAYMENT_WAITING ||
                state == OrderState.PREPARING;
    }
}

public enum OrderState{
    PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETE;
}

 

1.6 엔티티와 밸류

도출한 모델은 크게 엔티티(Entity)와 밸류(Value)로 구분할 수 있다.

 

1.6.1 엔티티

엔티티의 가장 큰 특징은 식별자를 가진다는 것이다.

식별자는 엔티티 객체마다 고유하다. 엔티티를 생성하고 속성을 바꾸고 삭제할 때까지 식별자는 유지된다. value 타입을 사용해서 의미 명확성을 높이면 좋다.

예시) 엔티티 : Order, 식별자 : OrderNumber

1.6.3 밸류 타입

밸류 타입은 개념적으로 완전한 하나를 표현할 때 사용한다. 코드 이해를 돕는다.

밸류 타입을 위한 기능을 추가할 수도 있다. 밸류 타입 클래스에 함수를 추가하는 것.

밸류 객체의 데이터를 변경할 때는 기존 데이터 변경이 아닌, 변경한 데이터를 갖는 새로운 밸류 객체를 생성해야 한다. 불변 객체로 생성해야 한다.(Setter 사용 X)

[밸류 타입 사용 전 예시]

package com.myshoptest;

public class ShippingInfoTest {
    private String receiverName;
    private String receiverPhoneNumber;
}

[밸류 타입 사용 후 예시]

package com.myshoptest;

public class ShippingInfoTest {
    private Receiver receiver;
}
package com.myshoptest;

import lombok.Getter;

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

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

 

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

도메인 객체가 불완전한 상태로 사용되는 것을 막으려면 생성 시점에 필요한 것을 전달해 주어야 한다. 생성자를 통해 필요한 데이터를 모두 받아야 한다.

[생성자에서 호출하는 set 메서드는 private로 선언하는 예시]

public class Order {
    
    public Order(OrderNo number, Orderer orderer, List<OrderLine> orderLines,
                 ShippingInfo shippingInfo, OrderState state) {
        setNumber(number);
        setOrderer(orderer);
        setOrderLines(orderLines);
    }

    private void setNumber(OrderNo number) {
        if (number == null) throw new IllegalArgumentException("no number");
        this.number = number;
    }

    private void setOrderer(Orderer orderer) {
        if (orderer == null) throw new IllegalArgumentException("no orderer");
        this.orderer = orderer;
    }

    private void setOrderLines(List<OrderLine> orderLines) {
        verifyAtLeastOneOrMoreOrderLines(orderLines);
        this.orderLines = orderLines;
        calculateTotalAmounts();
    }
}