도메인 주도 개발 시작하기 책 정리
네 개의 영역
✅ 표현
사용자의 요청을 받아 응용 영역에 전달하고 응용 영역의 처리 결과를 다시 사용자에게 보여주는 역할
-> 스프링 MVC가 표현 영역을 위한 기술에 해당한다.
✅ 응용
시스템이 사용자에게 제공해야 할 기능을 구현
-> 도메인 로직을 직접 수행하기보다는 도메인 영역에 로직 수행을 위임한다.
✅ 도메인
도메인의 핵심 로직(도메인 모델)을 구현한다.
✅ 인프라스트럭처
구현 기술에 대한 것을 다룬다.
계층 구조 아키텍처
✅ 계층 구조
네 개의 영역을 구성할 때 많이 사용하는 아키텍처
구현의 편리함을 위해 계층 구조를 유연하게 적용하기도 한다.
응용 계층에서 외부 시스템과의 연동을 위해 인프라스트럭처 계층에 의존하기도 한다.
✅ 인프라스트럭처 의존
이런 구조에서 짚고 넘어가야 할 것은 응용, 도메인 계층의 인프라스트럭처 의존이다.
1. 만약 구현 기술이 바뀌게 되면 이를 직접 의존하고 있는 응용, 도메인 계층 역시 코드를 수정해야 한다.
-> 이는 애초에 응용, 도메인 계층에 기술 영역이 포함되었다는 뜻이다.
2. 만약 인프라스트럭처를 직접 의존하고 있는 응용 서비스를 테스트하고싶다면, 구현 기술과 관련된 부분이 세팅이 안되면 응용 서비스를 테스트할 수 없다.
정리하면, 인프라스트럭처에 의존하면 '기능 확장의 어려움'과 '테스트 어려움'이라는 두 가지 문제가 발생한다.
-> 이에 대한 해답은 DIP에 있다.
DIP
✅ 모듈 수준
1. 고수준 모듈: 의미 있는 단일 기능을 제공, 여러 하위 기능이 필요하다.
(ex. 응용계층: 가격 할인 계산 -> 하위기능: 고객 정보를 구하고(1), 룰을 이용해서 할인 금액을 구한다(2))
2. 저수준 모듈: 하위 기능을 실제로 구현.
(ex. 인프라스트럭처 계층: RDB에서 JPA로 고객 정보를 구한다(1), Drools로 룰을 적용한다(2))
✅ DIP
고수준 모듈이 동작하려면 저수준 모듈을 사용해야 한다.
-> 그러나, 이런 경우 앞서 보았듯이 '기능 확장의 어려움'과 '테스트 어려움'이 있다.
DIP는 이 문제를 해결하기 위해 저수준 모듈이 고수준 모듈에 의존하도록 바꾼다.
how? 인터페이스 사용
고수준 모듈에 인터페이스를 두고, (이 부분이 중요하다)
저수준 모듈에 실제 기술을 사용하는 인터페이스 구현 객체를 위치시키면,
1. 기능 확장의 어려움
구현 기술을 변경하더라도 고수준 모듈은 수정할 필요가 없다.
저수준 모듈의 구현 객체를 생성하는 코드만 변경하면 된다.
2. 테스트 어려움
실제 기술을 사용하는 저수준 모듈의 구현 클래스가 없어도,
인터페이스에 mock 객체를 주입받아서 테스트가 가능하다.
DIP 주의사항
✅ 저수준이 고수준을 의존하도록
DIP는 단순히 인터페이스와 구현 클래스를 분리하는 것이 아닌,
고수준 모듈이 저수준 모듈에 의존하지 않도록 하는 것이다.
앞서 강조한 것처럼 하위 기능을 추상화한 인터페이스는 고수준 모듈 관점에서 도출되기 때문에, (어떤 기술을 사용하는지 궁금하지 않아)
인터페이스는 고수준에 위치시켜야 한다.
DIP와 아키텍처
DIP를 적용시키면 인프라스트럭처 영역이 응용 영역과 도메인 영역에 의존하게 된다.
+) DIP는 항상 적용할 필요 없다.
때로는, 구현 기술에 의존적인 코드가 효과적일 때도 있다.
-> DIP의 이점을 얻는 수준에서 적용 범위를 검토해 보자.
도메인 영역의 주요 구성요소
엔티티와 밸류
✅ 도메인 모델의 엔티티
DB 모델의 엔티티는 테이블의 데이터를 갖는 역할을 한다면,
도메인 모델의 엔티티는 데이터와 함께 도메인 기능을 제공한다.
또한, 테이블과 달리 도메인의 이해를 위해 밸류 타입들도 자유롭게 사용할 수 있다.
애그리거트
✅ 애그리거트 장점
도메인 모델이 커질수록 많은 엔티티와 밸류가 출현하는데,
이를 애그리거트로 묶어 상위 수준에서 모델을 볼 수 있어야,
전체 모델의 관계와 개별 모델을 이해하는 데 도움이 된다.
✅ 루트 엔티티
애그리거트에 속한 객체들을 관리하는 엔티티.
-> 루트 엔티티는 애그리거트에 속해 있는 객체들을 이용해서 애그리거트가 구현해야 할 기능을 제공한다.
애그리거트를 사용하는 코드는 루트 엔티티를 통해서만 간접적으로 애그리거트 내의 다른 엔티티나 밸류에 접근한다.
-> 애그리거트의 내부 구현을 캡슐화
리포지터리
✅ 리포지터리가 도메인 영역?
엔티티나 밸류가 요구사항에서 도출되는 도메인 모델이라면,
리포지터리는 구현을 위한 도메인 모델이다.
-> 도메인 객체를 영속화하는데 필요한 기능을 추상화 한것으로 고수준 모듈에 속한다.
-> 리포지터리를 구현한 클래스는 저수준 모듈로 인프라스트럭처 영역에 속한다.
요청 처리 흐름
✅ 표현 영역
1. 사용자가 전송한 데이터 형식이 올바른지 검사한다.
2. 사용자가 전송한 데이터를 응용 서비스가 요구하는 형식으로 변환해서 전달한다.
✅ 응용 영역
1. 도메인 모델을 이용해서 기능을 구현한다. (ex. 리포지터리를 이용해 도메인 객체를 가져와 실행하거나, 신규 도메인 객체를 생성한다)
2. 도메인의 상태를 변경하므로 트랜잭션을 관리한다. (ex. @Transactional)
인프라스트럭처 개요
✅ 인프라스트럭처 의존
앞서 DIP를 설명할 때, [변경의 유연함, 테스트가 쉬움]을 위해 인프라스트럭처에 대한 의존을 없앤다고 말했는데,
'무조건' 인프라스트럭처에 대한 의존을 없앨 필요는 없다.
예를 들어,
1. 응용 서비스는 트랜잭션 처리를 위해 스프링이 제공하는 @Transactional을 사용하는 것이 편리하다.
2. 도메인 엔티티는 JPA가 제공하는 @Entity나 @Table을 사용하는 것이 편리하다.
-> [구현의 편리함]은 DIP가 주는 장점인 [변경의 유연함, 테스트가 쉬움]만큼 중요하기 때문에,
일정 범위 내에서 응용 영역과 도메인 영역에서 구현 기술에 대한 의존을 가져가는 것은 나쁘지 않다.
모듈 구성
✅ DDD의 패키지 구조
1. 기본적으로 아키텍처 영역 별로 별도 패키지를 구성한다.
2. 도메인이 크면 하위 도메인 별로 패키지를 구성한다.
3. 도메인 패키지는 도메인에 속한 애그리거트를 기준으로 패키지를 구성한다.
4. 도메인 패키지에는 애그리거트, 모델, 리포지터리(인터페이스)를 위치시키고, 필요에 따라 도메인 서비스를 위치시킨다.
'book > 도메인 주도 개발 시작하기' 카테고리의 다른 글
[DDD Start] 응용 서비스와 표현 영역 (0) | 2023.08.12 |
---|---|
[DDD Start] 스프링 데이터 JPA를 이용한 조회 기능 (0) | 2023.08.12 |
[DDD Start] 리포지터리와 모델 구현 (0) | 2023.07.28 |
[DDD Start] 애그리거트 (0) | 2023.07.18 |
[DDD Start] 도메인 모델 시작하기 (0) | 2023.07.06 |