Home DDD (도메인 주도 설계, Domain-Driven Design)
Post
X

DDD (도메인 주도 설계, Domain-Driven Design)

DDD (Domain-Driven Design)

도메인 주도 설계란 복잡한 비즈니스 로직을 기술 중심이 아니라 도메인(업무 영역) 중심으로 설계하는 접근법입니다.


핵심 철학

단순한 설계 패턴이 아니라, 시스템을 바라보는 관점 자체를 바꾸는 것 “단순히 코드 구조를 깔끔히 나누자”가 아닌 “비즈니스 언어를 코드로 표현하자”

  • 도메인 중심 사고 : 데이터 구조가 아닌, 비즈니스 규칙과 개념을 중심으로 모델링
  • 언어의 일관성 (유비쿼터스 언어) : 도메인 전문가와 개발자가 같은 용어로 대화
  • 경계 설정 (Bounded Context) : 도메인을 여러 하위 영역으로 분리해 복잡도 관리
  • 모델과 코드의 일치 : 코드 구조가 실제 도메인 구조를 그대로 반영
  • 변화에 강한 구조 : 기술 변경(DB, ORM 등)에도 도메인 로직이 영향을 받지 않음

즉, DDD의 핵심은 “코드와 비즈니스 언어의 일치”입니다.


핵심 용어


도메인(Domain)

  • 시스템이 해결하려는 비즈니스 영역

영화 예매 시스템 : 예매, 결제, 회원, 상영


엔티티(Entity)

  • 고유한 식별자(ID)를 가지는 객체
  • 속성 값이 바뀌어도 동일한 객체로 간주
  • 비즈니스 규칙이 직접 담기며, 상태 변화가 허용
1
2
3
4
5
6
class Booking {
  constructor(public id: string, public userId: string, public seat: string) {}
  cancel() {
    // 예매 취소 규칙
  }
}

값 객체(Value Object)

  • 식별자가 없고, 값으로만 구분되는 객체
  • 불변(Immutable)이며, 의미 단위를 표현
  • 여러 엔티티에서 공유 가능한 개념
1
2
3
4
5
6
7
8
class Money {
  constructor(private amount: number, private currency: string) {}

  add(value: Money): Money {
    if (this.currency !== value.currency) throw new Error("통화 단위 불일치");
    return new Money(this.amount + value.amount, this.currency);
  }
}

Aggregate (집합체)

  • 여러 Entity와 Value Object를 하나로 묶은 단위
  • 비즈니스 규칙상 함께 변경되어야 하는 객체들의 집합
  • 불변성의 단위를 보장하는 루트 엔티티(Aggregate Root)를 갖음
1
2
3
4
BookingAggregate
 ├── Booking (Root Entity)
 ├── Payment (Entity)
 └── Seat (Value Object)

Repository (저장소)

  • Aggregate 단위로 데이터를 영속화하고 조회하는 역할
  • 기술적인 DB 접근 로직(Infrastructure)을 도메인에서 분리
1
2
3
4
interface BookingRepository {
  findById(id: string): Promise<Booking>;
  save(booking: Booking): Promise<void>;
}

Domain Service

  • 여러 Entity나 VO가 협력해야 하는 도메인 로직을 담당
  • 특정 엔티티 하나에 속하지 않는 순수 비즈니스 규칙
1
2
3
4
5
6
7
8
class BookingService {
  constructor(private readonly paymentService: PaymentService) {}

  async reserve(booking: Booking, seat: Seat) {
    if (!seat.isAvailable()) throw new Error("좌석이 이미 예약되었습니다.");
    await this.paymentService.pay(booking);
  }
}

Application Service

  • 사용자의 유스케이스(Use Case)를 수행하는 계층
  • 트랜잭션 단위로 동작하며, 여러 도메인 서비스를 조합
  • 비즈니스 로직은 없고, 흐름 제어 집중
1
2
3
4
5
6
7
8
9
10
11
12
class BookingApplicationService {
  constructor(
    private readonly bookingRepo: BookingRepository,
    private readonly bookingService: BookingService
  ) {}

  async reserve(userId: string, seatId: string) {
    const booking = new Booking(userId, seatId);
    await this.bookingService.reserve(booking, seatId);
    await this.bookingRepo.save(booking);
  }
}

Infrastructure

  • DB, 외부 API, 메일 서비스 등 기술적 세부 구현을 담당
  • 도메인 계층에 의존하지 않도록 인터페이스 기반으로 추상화
1
2
3
4
5
6
class PrismaBookingRepository implements BookingRepository {
  async findById(id: string): Promise<Booking> {
    const data = await prisma.booking.findUnique({ where: { id } });
    return new Booking(data.id, data.userId, data.seat);
  }
}

유비쿼터스 언어 (Ubiquitous Language)

  • DDD에서 가장 중요한 원칙 중 하나
  • “도메인 전문가와 개발자가 같은 언어로 이야기해야 한다.”
비즈니스 용어코드 표현
예매 취소cancelBooking()
좌석 점유reserveSeat()
결제 승인approvePayment()

언어의 일관성이 유지될수록 시스템을 이해하기 쉬워집니다. 이는 단순한 네이밍 규칙이 아니라, 커뮤니케이션과 설계의 일관성을 유지하기 위한 핵심 원칙입니다.


장점

  • 복잡한 비즈니스 로직의 명확화 : 규칙과 개념이 코드로 드러남
  • 유지보수성 향상 : 각 계층이 독립적이며 수정 영향이 작음
  • 테스트 용이성 : 도메인 로직만 단독으로 테스트 가능
  • 변화에 유연함 : DB, ORM, 프레임워크 변경에도 도메인 불변
  • 팀 커뮤니케이션 향상 : 도메인 언어가 통일되어 협업 효율 증가

계층 구조 (Layered Architecture)

DDD는 보통 4계층 구조로 표현됩니다.

계층역할예시
Presentation Layer사용자 요청/응답 처리 (HTTP, GraphQL 등)Controller
Application Layer유스케이스 조합 및 실행Application Service
Domain Layer핵심 비즈니스 로직, 규칙Entity, Value Object, Domain Service
Infrastructure LayerDB, 외부 API, 기술 의존 구현Repository, Adapter

MVC와의 차이

비교 항목전통적인 3계층 (MVC)DDD (4계층 구조)
중심 사고기술 중심비즈니스 중심
모델단순 데이터 구조풍부한 도메인 모델
로직 위치Controller / Service 혼재Domain / Application으로 명확히 분리
확장성비즈니스 커지면 복잡도 급증계층별로 분리되어 유지보수 용이
테스트통합 테스트 중심단위 테스트 가능
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.