🐱 Meow, meow

도메인 주도 설계(DDD, Domain-Driven Desigin) 는 단순한 방법론이나 프로세스가 아닌, 도메인 중심의 개발 접근법이다.
그렇다면 핵심 개념에 대해서 알아보고, 정리해보자.

Intro


들어가기에 앞서서, 해당 내용은 Nextstep 에서 진행하는 조영호님의 강의인 “도메인 주도 설계의 사실과 오해” 에서 배운 내용을 요약하고 정리한 부분이 포함되어 있다. 해당 강의는 책으로 읽었던 내용들을 다시 정리할 수 있었던 부분들이 많았고, 개념정리를 다시 할 수 있어서 좋았는데, 개인적으로는 연차를 막론하고 적극 추천한다.


도메인 주도 설계(DDD)의 본질


ddd본질


접근법

  • DDD 는 프로세스나 방법론이 아니라 패턴의 집합이며, 자신의 상황에 맞는 것을 선택적 으로 적용 해야 한다.

모델링 사이클

  • 도메인 모델 = 코드 모델
  • 도메인이 바뀌면 코드가 바뀌고, 코드가 바뀌면 도메인 모델이 바뀌는 사이클을 가지고 있다.

구현 가이드

  • 빌딩 블록을 통해 도메인의 개념을 코드로 옮기고 복잡도를 낮춘다.

불변식 기반

  • DDD 는 불변식이라는 것이 깔려있는데, 기능 요구사항과 불변식을 어그리게이트 단위로 구현한다.


엔티티(Entity)와 값 객체(Value Object)


엔티티 (Entity) 값 객체 (Value Object)
식별성이 중요 속성이 중요
가변 (상태 변경 가능) 불변 (변경 불가)
식별자로 동일성 비교 속성으로 동등성 비교
생명 주기 추적 필요 대체 가능 / 엔티티의 복잡성 감소


엔티티 (Entity)

  • 식별성이 중요하며, 상태 변경이 가능하다.
  • 생명 주기를 관리해야하며, 식별자동일성을 비교한다.
  • 트래킹이 필요하면 엔티티로 모델링한다.
  • 엔티티는 식별자로 불리기도 하는데, 가변 객체이며 상태를 변경하기 위해서 만드는 것이다.

값 객체

  • 속성 이 중요하며, 불변성 이 유지되어야 한다.
  • 속성이 동일하면 같은 객체로 취급한다. (동등성 비교)
  • 엔티티의 복잡성을 줄이는 역할을 수행한다.
  • 같은 값이 A 클래스, B 클래스 에서 사용되면 값 객체로 추출하면 된다.
📢 상태 변경을 엔티티에 몰아주고 값을 모두 빼버리면 심플해지고, 선언적으로 코드가 짜여진다.


어그리게이트 (Aggregate)


aggregate


정의

  • 여러 엔티티와 값 객체를 하나로 묶는 단위이다.

경계 설정

  • Aggregate 는 캡슐화 경계를 형성 하며, 외부에서는 루트 엔티티만 참조 가능 하다.

트랜잭션 단위

  • Aggregate 단위로 트랜잭션을 유지한다.

불변식

  • 트랜잭션과 일관성이 있는데, “불변식을 한번에 처리하겠어” 이게 Aggregate 이다.
📢 루트 엔티티는 전역 식별성을 가지고 있고, 궁극적으로 불변식을 검사할 책임이 있다.
📢 경계 안의 엔티티는 지역 식별성을 지니고 있으며, 밖에선 무조건 루트 엔티티만 참조 가능하다.


리포지토리 (Repository)


역할

  • Aggregate 단위(DDD 에서는 객체 단위가 아님)로 영속성 을 관리하며, 데이터 저장 및 검색을 담당한다.
  • DDD 에서는 원래의 Repository 는 컬렉션과 비슷한데, 메모리상에 객체가 있는 것처럼 쓰는 객체인데, 요즘에는 그냥 Database 라고 보면 된다.

설계 원칙

  • 객체 단위가 아닌 Aggregate 단위 로 리포지토리 생성한다. (Aggregate 당 하나의 Repository)
  • ID 를 통해 외부에서 참조하며, 내부에서는 객체를 참조한다.


연관 관계 설계 원칙


설계원칙


✅ 단방향 선호
✅ N:1 관계 선호
✅ 불필요한 연관관계 제거

❌ 양방향 지양
❌ 1:N 관계 지양

접근 방법

  • 도메인은 무조건 행위 인 것이다. 행위 관점에서 연관관계가 있는지를 확인해야한다.

단방향 참조 선호

  • 양방향 참조는 복잡성을 증가시키므로 가급적 단방향으로 설계한다.

N:1 관계 선호

  • 1:N 관계는 지연 로딩 문제로 인해 선호하지 않는다.

불필요한 연관관계 제거

  • ID 참조를 통해 외부 참조를 최소화하고, Aggregate 내부에서는 객체 참조를 통해 탐색한다.


서비스 계층 구조


서비스계층구조


분류

  • DDD 에서는 서비스를 인프라 서비스, 애플리케이션 서비스, 도메인 서비스로 나눈다.

애플리케이션 서비스

  • 일반적으로 우리가 알고 있는 서비스를 의미한다.

도메인 서비스

  • 도메인 로직에서 서비스는 도메인 로직을 Aggregate 로 넘기기 애매하면 서비스로 만들라는 것이다.

인프라 서비스

  • Database 접근, 외부 시스템 통합, 도메인 및 애플리케이션 계층이 기술적 세부 사항에 의존하지 않도록 추상화 계층을 제공한다.
  • 로깅, 모니터링, 캐싱, 메시지 큐 관리 등 같은 시스템 기반 서비스를 제공한다.


설계 접근법


설계접근법


설계 방법

  • 설계 할 때 책임 주도 설계를 하고, (객체 지향 언어를 사용하니깐) 계약에 의한 설계를 따른다.

기본 원칙

  • 책임 주도 설계 → 객체 지향 / 계약에 의한 설계 → 불변식

DDD 적용 사이클과 애자일과 비슷한 점


DDD 적용 사이클


DDD 의 적용 흐름 사이클은 애자일 방법론과 매우 밀접하게 연관이 있다.
두 접근법 모두 반복적이고 점진적인 개선을 중요시 한다는 특성이 있다.


📌 반복적 개발

  • 한 번에 완벽한 시스템을 구축하기 보다는 반복적인 사이클 을 통해 점진적 으로 시스템을 발전 시키는 접근법이다.

📌 지속적인 피드백

  • 애자일에서 중요시하는 지속적인 피드백 루프가 DDD 의 테스트 - 리팩토팅 - 도메인 분석 순환과 유사하다.

📌 협업 중심

  • 애자일은 개발자와 이해관계자 간의 긴밀한 협업을 강조하고, DDD 는 도메인 전문가와 개발자 간의 지식 공유와 협업이 핵심이다.

📌 변화 수용

  • 애자일과 DDD 모두 요구사항과 이해의 변화를 자연스럽게 수용하는 프레임워크를 제공한다고 볼 수 있다.

📌 점진적 모델 개선

  • 애자일의 점진적 개발 방식은 DDD 에서 도메인 모델을 점진적으로 발견하고 개선해 나가는 과정과 동일하다고 볼 수 있다.

📘 DDD 의 가치

📌 도메인 중심 접근
   ✏️ DDD 는 도메인 쪽으로 찾으려고 하는 것이지, 기술적으로 접근하게 되면 사람들의 의견이 달라서 맞추기 힘들다.
📌 일관성
   ✏️ 도메인이라는 기준으로 나누면 그대로 일관성 있게 갈 수 있다.
📌 시스템 분할
   ✏️ 전체적으로 시스템을 나누거나 구현할 때 조금은 쉬울 수 있다.


실용적 적용 Tips


📌 결정의 유연성

  • 도메인에서 이게 엔티티고, 값 객체이다 이런 정형적인 답은 없다. 상황에 따라서 정하는 것이다.

📌 점진적 접근

  • 어떤걸 엔티티로, 어떤걸 값 객체로 해야할지 잘 모르겠다면, 값 객체로 모델링하고 엔티티로 올리면 된다.

📌 값 객체 추출

  • 처음부터 값 객체를 뜯어내서 개발하는 경우는 잘 없다.
  • 리팩토링을 할 때 같은 값이 A 클래스, B 클래스에서 사용된다면 값 객체로 신분을 상승시키면 된다.

📌 Aggregate 설계

  • Aggregate 설계는 기능 구현과 불변식을 고려해서 설계한다.



📗 요약

🖐 DDD 는 단순한 방법론이 아닌 복잡한 도메인을 코드로 명확하게 표현하기 위한 사고방식이다.
🖐 DDD 의 핵심은 도메인 모델을 코드로 일관되게 유지하고 복잡성을 줄이고 불변식을 관리하는 것이다.
🖐 적용할 때는 패턴을 강박적으로 따르는 것보다는 도메인의 특성과 프로젝트의 필요에 맞춰서 유연하게 차용해서 사용하면 된다.
   ➡️ 오버엔지니어링을 경계해야한다. 그래서 도메인 특성과 프로젝트의 필요에 맞추라는 것.
🖐 애자일과 유사하게 점진적으로 모델을 개선하는 방식으로 접근하는 것이 현실적인 DDD 적용의 핵심이다.



Reference