본문 바로가기
IT 교양

Domain-Driven Design: 도메인 주도 설계

by 진기씨 2025. 2. 21.
반응형

오늘도 내용과 무관한 덴마크 항구 풍경

 

소프트웨어 개발에서 비즈니스 요구사항을 충실히 반영하는 것은 매우 중요하다.

하지만 개발자와 비즈니스 전문가 사이 의사소통 단절로 인해 요구사항이 명확하지 않거나 구현 과정에서 본래의 의미가 왜곡되어 프로젝트가 산으로 가는 경우가 종종 발생한다. 
이런 문제를 해결하기 위해 등장한 개념이 바로 도메인 주도 설계(Domain-Driven Design, DDD)이다.

도메인 주도 설계(DDD)는 소프트웨어의 핵심을 '도메인(비즈니스 영역)'에 집중하여 설계하는 방법론이다.  
즉, 단순히 기능을 나열하는 것이 아니라, 도메인의 핵심 개념, 용어, 규칙을 반영하여 모델링하고 이를 코드로 직접 구현하는 방식이다.  

이를 위해 개발자 뿐 아니라 기획자, 도메인 전문가를 함께 모아 다수의 회의를 진행한다.


이렇게 여러 사람이 모여서 만들게 되는 것이 도메인 모델로, DDD의 핵심이라고 볼 수 있다.
도메인 모델이란, 이해 관계자들이 공통의 언어(Ubiquitous Language)를 사용하여 정의한 비즈니스 개념과 규칙을 코드로 표현한 것이다.

도메인 모델은 두 수준의 설계를 통해 완성된다.

 

전략 설계

전략 설계는 비지니스 로직이 포함된 상위 수준의 결정들을 의미한다.

각자 전문 영역이 다른 이해 관계자들의 협업을 체계적으로 진행하기 위한 방법론으로 이벤트 스토밍(Event Storming)을 활용한다.
이벤트 스토밍은 도메인의 동작을 이벤트 중심으로 시각화하는 방법으로, 참가자들이 포스트잇을 활용하여 비즈니스 프로세스를 분석하고 개선하는 과정을 거친다.
이 과정에서 참가자들은 각 단계마다 특정 질문을 던지며, 도메인의 핵심 개념을 구체화 해나간다.

아래의 질문들을 순서대로 따라 답변을 만들어나간다.

 

"우리가 해결하려는 문제는 정확히 무엇인가?"

"우리의 솔루션이 전해주는 가치가 무엇인가?"

"비즈니스의 핵심 목표는 무엇인가?"
"시스템에서 어떤 주요 이벤트들이 발생하는가?"
"이벤트는 어떤 순서로 일어나는가?"
"이벤트들은 서로 어떤 연관이 있는가?"

 

이러한 이벤트를 나열하면서, 비즈니스의 핵심 흐름을 이해하고, 이벤트 간의 관계를 정리한다.

다음으로는 이벤트를 발생시킬 사용자의 동작들을 정의한다.

 

"이 이벤트를 발생시키기 위해 사용자가 수행해야 하는 행동은 무엇인가?"
"이 행동을 수행하기 위해 필요한 입력 정보는 무엇인가?"
"이 행동을 실행할 때 검증해야 하는 조건은 무엇인가?"
"이 행동이 실패할 수 있는 이유는 무엇인가?"

 

애그리게이트는 이벤트의 일관성을 보장하기 위해 관련된 데이터를 하나의 단위로 묶는 것이다.
이 단계에서는 어떤 엔티티들이 묶여야 하는지, 어떤 객체가 애그리게이트 루트(Aggregate Root)인지를 결정해야 한다.

 

"이 이벤트를 저장하고 관리할 객체는 무엇인가?"
"어떤 데이터가 함께 변경되어야 하는가?"
"이 애그리게이트의 상태를 변경하는 규칙은 무엇인가?"
"이 애그리게이트를 생성하거나 수정하는 권한은 누구에게 있는가?"

 

마지막 단계에서는 시스템 간의 상호작용과 정책(Policy)을 정의한다.
이 단계에서는 외부 API, 데이터 흐름, 보안 정책 등을 고려해야 한다.

"이벤트가 발생했을 때 다른 시스템에 어떤 영향을 미치는가?"
"이벤트는 외부 시스템과 어떻게 연동되는가?"
"트랜잭션이 실패하면 어떻게 복구할 것인가?"
"어떤 데이터가 외부 API에 전달되어야 하는가?"

 

이 과정 중에 도메인을 세분화하여 핵심 가치와 관련된 코어 섭도메인, 이를 지원하는 부가 섭도메인, 완전한 외부의 섭도메인을 정의할 수 있다.

 

전술 설계

앞에서의 전략 설계를 바탕으로 어떻게 기술적으로 구현해 나갈지를 정의하는 해결방안을 만들어 나가는 과정이다.

컨텍스트를 제한해 가면서 앞에서의 이벤트, 엔티티, 애그리게이트 등을 코드로 변환하는 것이 목표이다.

 

엔티티(Entity)
- 고유한 식별자(ID)를 가지며, 시간이 지나도 변경되지 않는 개념

 

값 객체(Value Object)
- 고유한 식별자가 없으며, 값 자체가 동일하면 같은 객체로 간주됨, 수정 불가(immutable)

 

애그리게이트(Aggregate) & 루트 엔티티(Aggregate Root)
- 여러 엔티티가 모여 하나의 논리적인 단위를 이루는 개념
- 애그리게이트 루트는 외부에서 접근할 수 있는 대표 엔티티로, 내부 엔티티는 루트를 통해서만 접근 가능

class Order {
    let id: UUID
    var items: [OrderItem]
    
    init(id: UUID = UUID(), items: [OrderItem]) {
        self.id = id
        self.items = items
    }
}

class OrderItem {
    let id: UUID
    let product: String
    var quantity: Int
    
    init(id: UUID = UUID(), product: String, quantity: Int) {
        self.id = id
        self.product = product
        self.quantity = quantity
    }
}


- 위의 예시에서 `Order`는 **애그리게이트 루트**이며, `OrderItem`은 `Order`를 통해서만 접근가능

리포지토리(Repository)
- 애그리게이트 단위로 데이터를 저장하고 불러오는 역할

 

도메인 서비스(Domain Service)
- 특정 엔티티에 속하지 않는 비즈니스 로직을 처리하는 서비스

 

 

도메인 주도 설계(DDD)는 단순한 소프트웨어 개발 방식이 아니라, 비즈니스 로직을 소프트웨어의 중심에 두는 패러다임이다.
모든 프로젝트에 DDD가 필요한 것은 아니지만, 복잡한 비즈니스 로직이 포함된 시스템이라면 충분히 고려할 가치가 있는 강력한 설계 방법론으로 보인다.

이번에 진행하는 프로젝트에 한 번 직접 적용해보고 싶어 이렇게 공부한 족적을 남겨본다.

반응형