[Book] DDD START!
최범균님의
DDD START!
정리 글입니다.
DDD START!
1. 도메인 모델 시작
- 도메인은 다수의 하위 도메인으로 구성된다. 각 하위 도메인이 다루는 영역은 서로 다르기 때문에 같은 용어라도 하위 도메인마다 의미가 달라질 수 있음
- 도메인에 따라 용어의 의미가 결정되므로, 여러 하위 도메인을 하나의 다이어그램에 모델링하면 안된다.
- 모델은
엔티티
와밸류
로 구분
엔티티
식별자
를 가짐- 식별자 생성 방식
- 특정 규칙에 따라 생성
- UUID 사용
- 직접 입력
- 일련번호 사용(시퀀스나 DB의 auto increment 사용)
- 식별자 생성 방식
밸류
- 개념적으로 완전한 하나를 표현할 때 사용
- 밸류 타입 사용 시 장점
- 개념적으로 완전한 하나를 잘 표현할 수 있음 (ie. 주소, 돈)
- 밸류 타입을 위한 기능을 추가할 수 있음
- 주로 불변 객체를 활용 : 코드 안정성 확보
엔티티와 밸류 타입 모두 set 메서드를 사용하지 않는 것이 좋다.
- 엔티티의 경우 set 메서드를 사용할 경우, 상태 변경 시 의미를 불명확하게 전달하는 행위를 제공하는 것이기 때문에 의도가 분명한 행위를 만들어 외부에 노출시키는 것이 좋다
2. 아키텍처 개요
🧑🏻💻 아키텍처 네 영역
- 표현
- 사용자의 요청을 받아 응용 영역에 전달
- 응용 영역의 처리 결과를 다시 사용자에게 전달
- 응용
- 시스템이 사용자에게 제공해야 할 기능 구현
- 로직을 직접 수행하기 보다는 도메인 모델에 로직 수행 위임
- 도메인
- 도메인 모델 구현
- 도메인 모델은 도메인의 핵심 로직 구현
- 인프라스트럭처
- 구현 기술에 대한 것을 다룸
- RDBMS 연동 처리, 메시징 큐에 메시지 전송/수신, DB 연동 등
🧑🏻💻 DIP
- Dependency Inversion Principle, 의존 역전 원칙
- 핵심 : 고수준 모듈이 저수준 모듈에 의존하지 않도록 하기 위함
- 고수준 모듈이 저수준 모듈을 사용하려면 고수준 모듈이 저수준 모듈에 의존해야 하는데, 반대로 저수준 모듈이 고수준 모듈에 의존하게 만듦
- ie. 인프라 영역의 룰엔진에 의존하는 응용 로직이 있음. 이렇게 되면 테스트와 로직 변경을 어렵게 만든다. 고수준의 interface를 만들고 응용 로직이 해당 interface에 의존하고, 저수준 모듈은 고수준 interface를 구현하면 이러한 문제를 해결할 수 있음
🧑🏻💻 도메인 영역의 주요 구성요소
요소 | 설명 |
---|---|
Entity | 고유의 식별자를 갖는 객체. 자신의 라이프사이클을 가짐. 도메인의 고유한 개념을 표현. 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공 |
Value | 고유의 식별자를 갖지 않는 객체. 주로 개념적으로 하나의 도메인 객체의 속성을 표현할 때 사용. 엔티티 뿐만 아니라 다른 밸류 타입의 속성으로도 사용 가능 |
Aggregate | 관련된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것. |
Repository | 도메인 모델의 영속성을 처리. |
Domain Service | 특정 엔티티에 속하지 않는 도메인 로직을 제공. 도메인 로직이 여러 엔티티와 밸류를 필요로 할 경우 도메인 서비스에서 로직 구현 |
Aggregate
- 관련 객체를 하나로 묶은 군집
- 루트 엔티티를 가짐
- 루트 엔티티는 애그리거트에 속해 있는 엔티티와 밸류 객체를 이용해서 애그리거트가 구현해야 할 기능을 제공
- 즉, 애그리거트 루트가 제공하는 기능을 실행하고 애그리거트 루트를 통해서 간접적으로 애그리거트 내의 다른 엔티티나 밸류 객체에 접근
🧑🏻💻 인프라스트럭처
- 표현, 응용, 도메인 영역을 지원
- 도메인 객체의 영속성 처리, 트랜잭션, SMTP 클라이언트, REST 클라이언트 등 다른 영역에서 필요로 하는 프레임워크, 구현 기술, 보조 기능을 지원
🧑🏻💻 모듈
- 도메인이 크면 하위 도메인별로 모듈을 나눔
- ie
- com.shop.catalog : ui-application-domain-infrastructure
- com.shop.order : ui-application-domain-infrastructure
- com.shop.member : ui-application-domain-infrastructure
- ie
- 도메인 모듈은 도메인에 속한 애그리거트를 기준으로 다시 패키지 구성
- ie
- com.shop.catalog.domain
- product
- category
- com.shop.catalog.domain
- ie
3. 애그리거트
- 관련된 객체를 하나의 군으로 묶음
- 장점
- 모델을 이해하는 데 도움을 줌
- 일관성을 관리하는 기준
- 복잡한 도메인을 단순한 구조로 만듦
- 애그리거트에 속한 객체는 유사하거나 동일한 라이프사이클을 가짐
애그리거트 루트
- 애그리거트에 속한 모든 객체가 일관된 상태를 유지하려면 애그리거트 전체를 관리할 주체가 필요한데 이 책임을 지는 것이 애그리거트의
루트 엔티티
- 애그리거트 루트가 아닌 다른 객체가 애그리거트에 속한 객체를 직접 변경하면 안된다 : 일관성이 깨짐
- 애그리거트 내부의 다른 객체를 조합해서 기능을 완성
- 애그리거트 루트는 상태뿐만 아니라
기능 실행을 위임
하기도 한다
트랜잭션 범위
- 한 트랜잭션에서는 한 개의 애그리거트만 수정
- 한 트랜잭션으로 두 개 이상의 애그리거트를 수정해야 한다면 애그리거트에서 다른 애그리거트를 직접 수정하지 말고 응용 서비스에서 두 애그리거트를 수정하도록 구현
리포지터리는 애그리거트 단위로 존재
- Order 와 OrderLine 각각 별도의 테이블에 저장한다고 해서 Order와 OrderLine을 위한 리포지터리를 각각 만들지 않음
4. 리포지터리와 모델구현(JPA 중심)
5. 리포지터리의 조회 기능(JPA 중심)
6. 응용 서비스와 표현 영역
7. 도메인 서비스
8. 애그리거트 트랜잭션 관리
9. 도메인 모델과 Bounded Context
10. 이벤트
서비스 로직 간의 강결합 문제 존재
- 에러 발생 시 트랜잭션 처리에 대한 기준
- 성능
- 의존하고 있는 타 서비스에 성능 문제가 발생할 경우 영향을 받음
- 설계 상 문제
- 로직이 뒤섞임 -> 변경에 영향을 받음 / 기능 추가 시 로직이 섞이고 트랜잭션 처리가 더 복잡해짐
이런 강결합을 없앨 수 있는 방법이 이벤트
를 사용하는 것
이벤트
이벤트
: 과거에 벌어진 어떤 것
이벤트가 발생한다는 것 = 상태가 변경됐다는 것. 이벤트가 발생하면 그 이벤트에 반응하여 원하는 동작을 수행하는 기능을 구현.
이벤트 관련 구성 요소
- 이벤트 생성 주체 : 엔티티, 밸류, 도메인 서비스와 같은 도메인 객체
- 도메인 로직을 실행해서 상태가 바뀌면 관련 이벤트를 발생함
- 이벤트 핸들러(이벤트 구독자) : 이벤트 생성 주체가 발생한 이벤트에 반응
- 생성 주체가 발생한 이벤트를 전달받아 이벤트에 담긴 데이터를 이용해서 원하는 기능 실행
- 이벤트 디스패처 : 이벤트 생성 주체와 이벤트 핸들러를 연결해 주는 것
- 이벤트 생성 주체는 이벤트를 생성해서 이벤트 디스패처에 전달. 디스패처는 해당 이벤트를 처리할 수 있는 핸들러에 이벤트를 전파.
- 구현 방식에 따라 이벤트 생성과 처리를 동기, 비동기로 실행하기 된다.
이벤트의 구성
- 이벤트 종류 : 클래스 이름으로 이벤트 종류 표현
- 과거시제 사용
- 이벤트 발생 시간
- 추가 데이터 : 이벤트와 관련된 정보
이벤트 용도
- 트리거
도메인의 상태가 바뀔 때 다른 후처리를 해야 할 경우 후처리를 실행하기 위한 트리거로 이벤트를 사용
이벤트는 다른 기능을 실행하는 트리거가 된다
- 서로 다른 시스템 간의 데이터 동기화
이벤트 장점
- 서로 다른 도메인 로직이 섞이는 것 방지
- 이벤트 핸들러를 사용하면 기능 확장도 용이
- 이벤트 핸드럴를 추가해서 도메인 로직에 영향 없이 기능 확장