DDD

1일차 개념 정리

join5 2026. 4. 23. 23:42
반응형

1. 이번에 이해한 핵심

DDD 1일차에서 가장 먼저 잡아야 하는 개념은 아래 4가지다.

  • Entity: 속성이 바뀌어도 계속 추적해야 하는 대상
  • Value Object: 동일성보다 값 자체와 그 값의 규칙이 중요한 객체
  • Aggregate: 일관성 규칙을 지키기 위한 경계
  • Factory: 처음부터 유효한 상태의 객체를 만들기 위한 생성 책임

즉, DDD를 처음 배울 때는 아래 질문으로 접근하면 된다.

  1. 이건 계속 추적해야 하는 대상인가?
  2. 이건 값 자체가 중요한가?
  3. 이 객체의 규칙은 어디서 지켜야 하는가?
  4. 생성 책임은 어디에 두어야 하는가?

2. Entity

정의

속성이 바뀌어도 같은 대상으로 계속 식별하고 추적해야 하는 객체다.

예시

  • 강사
  • 강의
  • 회원
  • 주문

예를 들어 강의 제목이나 정원이 바뀌어도 그 강의는 여전히 같은 강의다.
중요한 것은 제목 값이 아니라 그 강의가 누구인가다.

예시 코드

class Course {
    Long id;
    String title;
    int capacity;
}

여기서 핵심은 title, capacity가 아니라 id다.

판단 기준

이 객체가 시간이 지나도 같은 대상으로 계속 추적되어야 한다면 Entity일 가능성이 높다.


3. Value Object

정의

객체의 동일성보다 값 자체와 값의 유효성 규칙이 더 중요한 객체다.

예시

  • 강의 제목
  • 금액
  • 정원
  • 수강기간
  • 주소

예를 들어 10000원은 “이 객체가 누구냐”보다 10000이라는 값이 맞는가가 중요하다.

예시 코드

class CourseTitle {
    private final String value;

    public CourseTitle(String value) {
        if (value == null || value.isBlank()) {
            throw new IllegalArgumentException("제목은 비어 있을 수 없습니다.");
        }
        this.value = value;
    }

    public String value() {
        return value;
    }
}

판단 기준

이 객체가 누구냐보다 값이 맞는지가 더 중요하다면 Value Object일 가능성이 높다.


4. Entity와 Value Object 구분

강의 관리 도메인 기준

Entity

  • 강사
  • 강의

Value Object

  • 강의 제목
  • 금액
  • 정원
  • 수강기간

헷갈리기 쉬운 포인트

정원, 수강기간은 강의에 붙어 있으니 Entity처럼 느껴질 수 있다.
하지만 DDD에서는 “강의에 속한 속성”이라고 해서 Entity가 되는 것은 아니다.

중요한 것은:

  • 정원이라는 값이 유효한가
  • 수강기간이라는 값이 올바른가

이지 정원 객체 자체를 추적하는 것이 아니다.

즉 이런 개념은 보통 Value Object 쪽에 가깝다.


5. Aggregate

정의

일관성 규칙(invariant)을 지키기 위한 경계다.

처음에는 아래처럼 이해하면 충분하다.

규칙을 한 군데에서 지키게 하려고 묶는 단위라고 보면 된다.

핵심 감각

Aggregate는 단순히 객체를 묶는 것이 아니라

  • 어떤 것들을 한 덩어리로 보고
  • 외부가 내부를 직접 마구 수정하지 못하게 하고
  • 그 안의 규칙과 상태 변경을 한 경계 안에서 관리하는 개념

이다.

강의 예시

예를 들어 강의에는 이런 규칙이 있다고 하자.

  • 제목은 비어 있으면 안 됨
  • 정원은 1명 이상이어야 함
  • 종료된 강의는 수정할 수 없음

이 규칙을 서비스 곳곳에서 제각각 검사하면 쉽게 깨진다.
그래서 Course가 자기 규칙을 직접 지키게 한다.

예시 코드

class Course {
    private Long id;
    private CourseTitle title;
    private int capacity;
    private CourseStatus status;

    public void changeTitle(CourseTitle newTitle) {
        validateUpdatable();
        this.title = newTitle;
    }

    public void changeCapacity(int newCapacity) {
        validateUpdatable();
        if (newCapacity < 1) {
            throw new IllegalArgumentException("정원은 1 이상이어야 합니다.");
        }
        this.capacity = newCapacity;
    }

    private void validateUpdatable() {
        if (status == CourseStatus.CLOSED) {
            throw new IllegalStateException("종료된 강의는 수정할 수 없습니다.");
        }
    }
}

이렇게 보면 Aggregate는 어려운 개념이라기보다
규칙을 어디서 보호할 것인가에 대한 경계라고 이해하면 된다.


6. Factory

정의

처음부터 유효한 상태의 객체를 만들도록 생성 책임을 모으는 것이다.

핵심 포인트

Factory의 본질은 단순히 new를 숨기는 것이 아니다.
핵심은 아래와 같다.

  • 생성 시점에 검증을 수행하고
  • 기본값을 넣고
  • 빠진 상태 없이
  • 일관된 객체를 만들게 하는 것

즉, 생성 책임을 한 곳에 모아서 정상 상태를 보장하는 것이 Factory의 핵심이다.

예시 코드

class Course {
    private CourseTitle title;
    private int capacity;
    private CourseStatus status;
    private LocalDateTime createdAt;

    private Course(CourseTitle title, int capacity, CourseStatus status, LocalDateTime createdAt) {
        this.title = title;
        this.capacity = capacity;
        this.status = status;
        this.createdAt = createdAt;
    }

    public static Course create(String title, int capacity, LocalDateTime now) {
        if (capacity < 1) {
            throw new IllegalArgumentException("정원은 1 이상이어야 합니다.");
        }

        return new Course(
                new CourseTitle(title),
                capacity,
                CourseStatus.READY,
                now
        );
    }
}

여기서 중요한 것은:

  • 제목 검증
  • 정원 검증
  • 기본 상태 설정
  • 생성 시각 설정

을 생성 시점에 강제했다는 점이다.


7. Factory와 정적 팩토리 메서드의 관계

여기서 헷갈리기 쉬운 부분이 있다.

먼저 결론

  • DDD의 Factory = 생성 책임에 대한 설계 개념
  • 정적 팩토리 메서드(static factory method) = 그걸 구현할 때 자주 쓰는 구현 방식

즉, Factory는 개념이고 정적 팩토리 메서드는 구현 수단 중 하나다.

왜 같은 말이 아닌가

Course.create(...) 같은 코드는 정적 팩토리 메서드다.
그리고 동시에 생성 책임을 모아 유효한 상태를 보장한다면 factory 역할도 할 수 있다.

하지만 생성 로직이 복잡해지면 꼭 엔티티 내부의 static 메서드로 처리하지 않고 별도 Factory 객체로 분리할 수도 있다.

정적 팩토리 메서드 예시

public static Course create(String title, int capacity, LocalDateTime now) {
    return new Course(
            new CourseTitle(title),
            capacity,
            CourseStatus.READY,
            now
    );
}

별도 Factory 객체 예시

class CourseFactory {
    public Course createDraft(String title, int capacity, Instructor instructor, LocalDateTime now) {
        CourseTitle courseTitle = new CourseTitle(title);

        return new Course(
                courseTitle,
                capacity,
                CourseStatus.DRAFT,
                now,
                instructor
        );
    }
}

구분 기준

정적 팩토리 메서드로 충분한 경우

  • 생성 로직이 단순하다
  • 해당 엔티티가 자기 생성 규칙을 표현해도 자연스럽다
  • 외부 협력 객체가 많지 않다

별도 Factory 객체가 더 나은 경우

  • 생성 과정이 길다
  • 여러 VO/Entity를 조립해야 한다
  • 여러 정책이나 도메인 규칙이 함께 들어간다
  • Aggregate 생성 책임이 복잡하다

8. 지금 단계에서 기억해야 할 최종 정리

한 줄 정의

  • Entity: 속성이 바뀌어도 계속 추적해야 하는 대상
  • Value Object: 동일성보다 값과 규칙이 중요한 객체
  • Aggregate: 일관성 규칙을 지키기 위한 경계
  • Factory: 처음부터 유효한 상태의 객체를 만들기 위한 생성 책임

압축 버전

DDD 1일차는 사실 아래 질문만 계속 던지면 된다.

  1. 이건 계속 추적해야 하는 대상인가?
  2. 이건 값 자체가 중요한가?
  3. 이 규칙은 어디서 보호해야 하는가?
  4. 생성 책임은 어디에 두어야 하는가?

9. 강의 관리 도메인으로 최종 요약

Entity

  • Instructor
  • Course

Value Object

  • CourseTitle
  • Capacity
  • CoursePeriod
  • Money

Aggregate

  • Course를 중심으로 제목 수정, 정원 변경, 상태 변경 규칙을 관리

Factory

  • Course.create(...)
  • 또는 필요 시 CourseFactory

10. 현재 이해 상태 요약

현재까지 이해한 내용을 자연스럽게 정리하면 다음과 같다.

추적해야 하는 대상은 Entity다.
값 자체에 가까운 개념은 Value Object다.
일관성 규칙을 지키는 경계는 Aggregate다.
생성 책임을 모아 유효한 상태를 보장하는 것은 Factory다.

반응형

'DDD' 카테고리의 다른 글

DDD 2일차 - Service와 Repository  (0) 2026.04.26