[도서 정리] 도메인 주도 개발 시작하기 - 최범균
최범균님의 도메인 주도 설계 관련 도서를 읽고 내용을 정리했다.
도메인 주도 DDD Start! 도메인 주도 설계 구현과 핵심 개념 익히기 (2016년)
도메인 주도 개발 시작하기 (2022년)
DDD Start를 보완하여 출판한 도서이기 때문에 도메인 주도 개발 시작하기를 보면 된다
새롭게 알게 된 개념
애그리거트
같은 생명주기를 공유하는 도메인들을 의미하며, 팩토리 메서드 등으로 루트엔티티에서 로직의 일관성을 지키며 관리하도록 한다
도메인 서비스
기존에도 서비스를 분리하거나 필요한 유틸들을 생성하여 사용했었지만 도메인 로직을 서비스에서 관리한다는 개념 자체는 단순하지만 생각하지 못했던 부분이었던 것 같다.
도메인 모델 로직을 한없이 늘리거나 응용 계층에서 일반 로직과 섞어서 복잡하게 짜지말고 도메인 서비스를 사용하는 것이 좋을 것 같다
인프라스트럭처
계층 아키텍처 기준으로 repository는 영속성 계층이었지만 도메인 주도 설계에서는 repository는 도메인 계층이며 실제 repository의 기능을 구현한 구현체는 인프라스트럭처 계층으로 나뉘었다
도메인 주도 설계에서는 고수준 모듈과 저수준 모듈을 구분하고 고수준은 interface 로직을 호출할 뿐 저수준 구현체와는 아무런 관계를 하지 않는다
도메인 주도 설계란, 도메인을 중심으로 설계하는 것이다
도메인이란 소프트웨어로 해결하고자 하는 문제 영역이다
쉽게 말해서 쇼핑몰 서비스라 하면 상품, 주문, 배송 등이 해당 서비스에서 해결하고자하는 문제 영역이라 할 수 있다
도메인과 엔티티는 다르다
도메인 모델 엔티티는 테이블만이 기준이 아니라 같은 단위의 속성도 묶는다
엔티티의 특징은 식별자를 갖는다는 것이다
Value의 특징은 값들의 묶음이라는 것이다
테이블이 있다하더라도 모두 엔티티는 아니다
한 엔티티에 포함되지만 DB 정규화에서 나눠진 테이블들이 있다
객체로 속성을 관리하게 되면 가독성이 높아지고 상태 관리의 일관성을 높일 수 있다
애그리거트
운명공동체인 객체의 묶음이다
루트 엔티티에서 기능을 제공한다
다른 객체에서 변경할 수 있도록 하면 일관성이 깨지고 중복 로직이 발생한다
루트 엔티티가 변경 주체이지만 실제 변경은 대상 객체에게 위임할 수 있다
트랜잭션 범위는 작을수록 좋으며, 한 트랜잭션에서 두 개 이상의 애그리거트를 건들지 않도록 한다
서비스에서 서로 다른 애그리거트를 호출하여 변경하도록 한다
JPA의 지연로딩의 개념과 같다. 즉시로딩은 객체 간의 결합도가 높아진다
N+1이 발생한다면 한 번에 호출하는 join 쿼리를 생성하여 조회하도록 한다
N:M 관계에서는 중간 테이블을 두어 다루는 것이 좋다
논리적인 하나의 도메인은 나열식으로 생성하기보다 하나의 애그리거트에서 팩토리 메서드로 함께 생성하는 것이 좋다
도메인 주도 설계에서의 계층
표현 계층, 응용 계층, 도메인 계층, 인프라 스트럭처 계층
고수준 모듈은 응용 계층과 도메인 계층이며, 저수준 모듈은 인프라스트럭처 계층이다
interface가 호출되는 곳은 고수준 모듈이어야한다
표현 계층, 응용 계층에 도메인 로직은 넣지 않도록 한다
응용 계층에서 중복 로직이 발생한다면 메서드로 분리할 필요가 있으며, 서비스 로직이 커진다면 서비스 클래스 수준에서 분리가 필요하다
응용 계층은 트랜잭션을 관리한다
응용 계층(서비스 계층)은 표현 계층에 필요한 데이터만 전달한다
계층 간 필요한 정보만 교환하고 서로 의존성을 줄인다
보통 표현 계층에서는 값을 검증하고, 응용 계층에서는 논리적 검증/존재 검증을 한다
리포지토리는 domain 계층이며, 리포지토리를 구현한 클래스는 infrastructure 계층이다
infrastructure 계층 : 영속성을 구현하거나 외부와 통신하는 기능을 제공하는 레이어
도메인 계층에는 도메인 모델과 도메인 서비스가 있다
저수준을 추상화하는 것을 경계해야한다!
저수준을 추상화하면 도메인이 구현체에 의존하게 된다
핵심 서비스 로직에서 -> 도메인 모델 로직과 부가 서비스 로직을 호출한다
주문과 할인 도메인 로직이 있을 때 주문에 할인된 금액 정보가 필요하다면 해당 로직을 주문 애그리거트에 할당하지 않아야한다
도메인 서비스를 생성하여 기능을 구현하는 것이 맞다 => 주문 도메인 서비스에서 할인금액을 계산하도록 한다
할인 로직이 달라지는 경우, 로직을 추가하기보다 interface로 구현체를 조립할 수 있다
Bounded Context; 모델은 컨텍스트(문맥)에서 결정된다
상품 모델은 카탈로그 상품, 주문 상품, 배송 상품 등 다양한 상품이 있다
각 컨텍스트에서의 상품이 각각의 모델이 된다
이벤트
이벤트가 발생할 때 후처리 관련한 트리거를 발생시키거나 데이터 동기화 작업을 한다
예를 들어 결제는 외부 서비스를 이용하는 경우가 많은데 외부 서비스에 문제가 생겼을 때 롤백을 할 것인지, 추후에 재시도할 것인지를 결정해야한다
이러한 경우 트랜잭션이나 여러 경우를 고려하여 로직을 짜게 되면 복잡한 로직으로 이어지게 되기 때문에 이벤트를 발생시켜 처리하도록 한다
spring에서 ApplicationEventPublisher와 @EventListener를 사용할 수 있다
비동기 처리는 다음과 같은 방식이 있다
코드 수준에서 @Async를 사용
메세지 큐 사용
이벤트 저장소 DB 및 별도 프로그램 구축
재처리 정책이나 멱등성 등을 고려하여 설계하도록 한다
CQRS
Command and Query Responsibility Segregation
명령과 조회의 책임 분리
DB에 대한 Write와 Read가 분리되는 것이다
데이터 변경이 있을 시 -> 메세지 이벤트 발생 -> 연관 데이터 변경
댓글을 작성해보세요.