개발

DDD START! 책 내용 정리

bebeside77 2022. 11. 21. 11:41

DDD START! 도메인 주도 설계 구현과 핵심 개념 익히기 책 내용 요약 정리 내용입니다.

 
DDD Start!
이 책은 DDD(도메인 주도 설계)를 처음 접하는 개발자를 위한 책이다. DDD를 실제 업무에 적용할 수 있도록 기본적인 이론을 설명하고 이를 구현한 코드를 바탕으로 입문자가 헤매지 않고 DDD를 학습할 수 있도록 했다. 애그리거트, 엔티티, 리포지토리 등 DDD의 핵심 패턴을 이용해서 도메인 모델을 구현하는 방법을 설명한다. 도메인 모델의 트랜잭션 충돌을 처리하는 기법(선점 잠금, 비선점 잠금)을 살펴보고, 도메인 이벤트를 이용해서 도메인 영역 간의 의존을 낮추는 방법과 명령 모델과 조회 모델을 나누는 기법에 대해서도 배운다. 스프링과 JPA를 이용해서 실제 동작하는 코드를 함께 제공하고 있으므로 DDD를 학습하고 싶은 개발자에게 좋은 길잡이가 될 것이다.
저자
최범균
출판
지앤선
출판일
2016.05.27

 

# 도메

도메인델은 기본적으로 도메 자체 이해하기 위념모델

개념델을용해서 바로 코드성할 있는 것은 아니기에 구술에 맞는 따로 필요함

 

## 도메인 아키텍처

표현 -> 응용 -> 도메 -> 인프라스트럭처 -> 데이터베이스, 메시 시스

 

## 엔티티와 밸류

엔티티 객체마다 고유 식별자는 갖는다

밸류는 엔티티 필드를 의미 맞게 생성함 (배송지 )

 

도메델에 습관적인 get/set 메서드 넣지 말자

 

# 아키텍처 개요

영역

  • (플리케이션이라면) HTTP 요청을 응용 영역이 필요로 형식으로 변환해서 전달
  • 응용 영역 응답을 HTTP 응답으로 변환해서 전송함

 

응용 서비스

  • 영역을 통해용자 전달받아용자에게공해야할
  • 기능현을 위해 도메 영역의 도메델을
  • 로직을 직접행하기보다는 도메델에행을임함

 

도메인 영역

  • 도메인의직을 구현하 도메인델을 구현한다

 

인프라스트럭처 영역

  • 구현술에 것을룬다
  • RBDMS 연동, 메시 메시지 전송/수

 

도메인/응용/표 영역은술을용한 코드 직접 만들지 않는다

 

## DIP (Dependency Inversion Principal)

고수듈이 저수듈에존성이 생기는 것을 막기 방법

응용 서비스가 인프라스트럭처의술에 종속적인 인터페이스를용하면

저수준듈에존성이 생기게 된다

고수준듈의점에서 저수듈에서 구현 인터페이스를 정의하여

문제 해결하 방법을 DIP라고 한다.

 

## 도메 영역의 주요 구성요소

  • 엔티티
  • 밸류
  • 애그리거트
  • 리포지터리
  • 도메 서비스

 

## 모

아키텍처의 영역은 별도 패키지에 위치

ui, application, domain, infrastructure

도메인이 도메인으로 나누고 도메인에서 다시 패키지로 분리

 

 

# 애그리거트

 

애그리거트에 속한 객체는 애그리거트에 속하지 않는다.

도메칙에 따라 함께 생성되는성요소는 애그리거트에 속할능성이 높음

함께 생성되고 함께 변경되는 엔티티가 같이 묶여야한다.

도메델을 만들기작하면 애그리거트로 보이 것이 많지만

경험이 생기고칙을 제대로 이해할수 크기 줄어든다.

다수 애그리거트가 하나 엔티티 객체만 갖고 드물게 두개 이 갖는다.

 

## 애그리거트 루트

애그리거트 루트 연관 엔티티를 가지고 있는 엔티티

애그리거트 루트 역할은 애그리거트관성 유지다.

위해 애그리거트가 제공해야 도메능을 구현한다.

애그리거트 루트가 아 객체가 애그리거트에 속한 객체를 직접 변경하면 안된다.

 

가지 습관적으로 적용

  • 단순 set 메서드 public 범위로 만들지 않는다
  • 밸류입은변으로 구현한다

 

## 트랜잭 범위

범위는 작을수록 좋다.

트랜잭션에서는 애그리거트정해야 한다.

트랜잭 충돌능성이 높아져서 전체 처리량이 떨어진다.

도메벤트를용해서 트랜잭션에서 애그리거트 변경 처리 있다.

 

## 리포지터리 에그리거트

리포지터리 애그리거트 단위로 존재

애그리거트내 엔티티를 물리적으로 별도 테이블에장하더라도 리포지터리 같이

리포지터리술에 따라 애그리거트 구현도 영향을 받는다.

 

## ID용한 애그리거트 참조

애그리거트에서 다 애그리거트 루트 가지고 있는 것은 좋지 않음

  • 트랜잭션에서 여러 애그리거트 변경
  • 성능 관련 필요 (JPA 사 , 즉시 로 결정 필요 )
  • 확장성 (도메인마다 DBMS 불가)

 

ID용해서 애그리거트 참조하면 문제 완능함!

 

### 조 성능

N+1 조 문제 발능해서 전용 쿼리 사 필요하다.

세타 조인을용해서 번의 쿼리로 필요한 데이터 로딩하는 방식

 

## 애그리거트 팩토리로

애그리거트가 갖고 있는 데이터용해서 애그리거트 생성한다면

애그리거트에 팩토리 메서드 구현하 것을 고려해보자.

(, 상점 애그리거트에서 상품 애그리거트 생성)

 

 

# 리포지터리 모델구(JPA 중심)

 

인터페이스는 애그리거트 루트준으로

 

@AttributeOverrides

AttributeConverter interface

@ElementCollection, @CollectionTable : 밸류 컬렉션을 별도 테이블로핑할

 

밸류입으로 식별자 구현하 식별자에 추가 있는 장점있음

 

별도 테이블로장되고 테이블에 PK 있다고 테이블핑되는

애그리거트성요소가 고유 식별자를 갖는 것은 아니다.

 

@SecondaryTable

 

 

# 리포지터리(JPA 중심)

 

CriteriaBuilder, Predicate

 

JPA 정적 메타 모

@StaticMetamodel

 

## 리포지터리 구

도메인델은술에 의존하지 않아야 한다.

현을상화하기 위해 많은력이 필요한데 실제 얻는점이 크지 않음

왜냐하면 리포지터리 구술을 정도의 변화 드물기 때

 

@Subselect, @Synchronize

 

 

# 응용 서비스 영역

## 응용 서비스

사용자 요청 처리를 위해 리포지터리로부터 도메인 객체를 구하고 도메인 객체를 사용한다.

 

주로 도메인 객체 간의 흐름 제어를 하므로 단순한 로직을 갖는다.

 

주된 역할 하나는 트랜잭션 처리이다.

 

응용 서비스에는 도메인 로직을 넣지 않는다. (도메인 영역에 넣기)

넣으면 코드 품질에 문제 발생 (응집성 떨어짐, 중복 코드 발생)

 

응용 서비스 역할 하나는 도메인 영역에서 발생시킨 이벤트를 처리하는

 

## 표현 영역

원칙적으로 모든 값에 대한 검증은 응용 서비스에서 처리한다.

하지만 표현 영역은 잘못된 존재 사용자에게 알려주고 다시 입력받아야

응용 서비스에서 항목에 대해 형식 확인 예외 발생 시키면 사용자는 한번에

하나의 오류가 있다는 것만을 알게되어 UX가 좋지 않다.

=> 응용 서비스에 전달 전에 표현 영역에서 모든 값을 검사한다.

 

표현 영역에서 필수 값과 값의 형식을 검사하면

응용 서비스는 아이디 중복 여부와 같은 논리적 오류만 검사하면 된다.

 

표현 영역 : 필수 , 값의 형식, 범위 검증

응용 서비스 : 데이터의 존재 유무와 같은 논리적 오류 검증

 

경험상 응용 서비스 실행 주체가 표현 영역이면 논리적 오류 위주로 검증해도 문제없었지만

주체가 다양하면 응용 서비스에서 반드시 논리적 외의 오류도 검사해야 한다.

 

 

# 도메인 서비스

애그리거트에 넣기 애매한 도메인 기능(, 결제 금액 계산 기능은 주문/회원/쿠폰 도메인 필요) 특정 애그리거트에 억지로 구현하면 안된다. (코드가 길어지고 외부 의존도가 높아짐)

복잡도가 높아지고 수정이 어려워진다.

이런 경우 도메인 서비스를 별도로 구현하는게 가장 쉬운 방법이다.

 

도메인 서비스 클래스를 구현하고 (, DiscountCalculationService)

애거리거트의 기능 메소드에서 도메인 서비스 객체를 받아 도메인 로직을 구현한다.

도메인 서비스 객체 생성해서 애그리거트에 전달하는 것은 응용 서비스의 책임

 

도메인 서비스는 도메인 로직을 수행하지 응용 로직을 수행하지는 않는다. 트랜잭션 처리와 같은 로직은 응용 서비스에서 처리해야 한다.

 

 

# 애그리거트 트랜잭션 관리

애그리거트 트랜잭션을 위해서는 DBMS 트랜잭션과 함께 추가적인 트랜잭션 처리 기법이 필요하다.

 

## 선점 잠금 (비관적 )

보통 DBMS의 단위 잠금(for update) 사용해서 구현한다.

JPA에서는 find 메서드에 LockModeType.PESSIMISTIC_WRITE 전달해서 적용

데드락 방지를 위해 javax.persistence.lock.timeout 활용 가능

 

## 비선점 잠금 (낙관적 )

선점 잠금 방식으로 모든 트랜잭션 충돌 문제 해결할 없음

선점 잠금 방식만으로는 update 처리 스레드 간의 충돌 문제만 방지 가능

수정 -> 수정 요청 흐름의 전체적인 잠금이 되지는 않기 때문

 

version 필드이블에 추가해서 수정 마다 +1하 조회한 version과 update 시 version이 동일한지 체크하는 방법

트랜잭션 충돌 OptimisticLockingFailureException 발

 

방법은 가지 케이스에 모두 적용 가능함

(수정 폼에 버전 정보 같이 전달)

 

### 강제 버전 증가

애그리거트 루트 외의 다른 엔티티의 값만 변경되는 경우 루트 엔티티의 버전 증가하지 않음 (JPA 특징)

증가하는게 애그리거트 관점에서 맞으므로 find 시 LockModeTypel.OPTIMISTIC_FORCE_INCREMENT 전달하여 강제 버전 증가 가능함

 

## 오프라인 선점 잠금

여러 사용자가 문서를 동시 수정하는 것을 방지하는 (수정 화면 동시 진입 막기)

선점 잠금, 비선점 잠금으로는 불가능, 오프라인 선점 잠금으로는 가능하다.

 

잠금 획득 해제하지 않고 프로그램 종료할 있기 때문에

잠금의 유효 기간을 두고 주기적으로 연장하는 방식으로 해야한다.

 

DB 정보를 만들어서 처리하는 방식으로 구현할 있다.

 

 

# 도메인 모델과 BOUNDED CONTEXT

 

하위 도메인마다 같은 용어라도 의미가 다르고 같은 대상이어도 지칭하는 용어가

다를 있음

따라서 개의 모델로 모든 하위 도메인을 표현하는 것은 어렵고 하위 도메인마다

모델을 만들어야 한다.

(, 회원의 Member 애그리거트 루트지만 주문의 Orderer 밸류임)

 

모델은 특정한 컨텍스트하에서 완전한 의미를 갖는다. 경계를 BOUNDED CONTEXT라 한다.

 

BOUNDED CONTEXT 표현 영역, 응용 서비스, 인프라 영역 등을 모두 포함한다.

(도메인 모델만 포함하는 것이 아님)

 

모든 BOUNDED CONTEXT메인 주도로 개발할 필요는 없다.

상품 리뷰 같은 단순한 도메인은 CRUD 방식으로 구현해도 유지보수하는데 문제가 없다.

 

## BOUNDED CONEXT 간 통합, 관계

 

## 컨텍스트

BOUNDED CONTEXT 간의 전체적인 관계를 표현한 . 시스템 전체 구조를 보여줌

 

 

# 이벤트

도메인 모델에 이벤트 도입을 위해 4 구성요소를 구현해야 한다.

  • 이벤트
  • 이벤트 생성 주체
  • 이벤트 디스패처
  • 이벤트 핸들러

 

이벤트를 사용하면 서로 다른 도메인 로직이 섞이는 것을 방지할 있다.

 

## 비동기 이벤트 처리

외부 시스템 연동을 동기로 성능, 트랜잭션 범위 문제를 해소하기 위해 비동기 처리하는 방법이 있다.

 

이벤트 비동기 구현 방법

  • 로컬 핸들러를 비동기로 실행하기
  • 메시지 큐를 사용하기
  • 이벤트 저장소와 이벤트 포워더 사용하기
  • 이벤트 저장소와 이벤트 제공 API 사용하기

 

 

# CQRS

Command Query Responsibillity Segregation

상태 변경을 위한 모델과 조회를 위한 모델을 분리하는

 

시스템 상태 변경할 때와 조회할 단일 도메인 모델을 사용하면

JPA 즉시 로딩, 지연 로딩 관련 고민거리가 생긴다.

JPA는 도메인 상태 변경 구현할 때는 적합하지만 여러 애그리거트를 조회하는 것은 고려할게 많아 구현을 복잡하게 한다.

 

복잡한 도메인에 적합하다.

 

) 상태 변경을 위한 도메인 모델 : Order (Orderer, OrderLine, ShippingInfo 포함)

목록 조회를 위한 모델 : OrderSummary (주문 관련 요약 정보만 포함)

 

명령 모델과 조회 모델이 서로 다른 DB를 사용하게 구현할 수도 있다.

DB 데이터 동기화는 이벤트를 활용해서 처리한다. (상황에 따라 동기, 비동기 이벤트로 처리)

 

## 장단점

장점

  • 명령 모델 구현 도메인 자체에 집중할 있다
  • 조회 성능을 향상시키는데 유리하다

단점

  • 구현해야 코드가 많다
  • 많은 구현 기술이 필요하다

 

이런 장단점을 고려해서 도입 여부를 결정해야 한다.