2.1 네 개의 영역
표현 영역 (Controller)
- HTTP 요청을 응용 영역이 필요로 하는 형식으로 변환해서 응용 영역에 전달하고 응용 영역의 응답을 HTTP 응답으로 변환하여 전송한다.
- HTTP 요청 파라미터로 전송한 데이터 → (응용 서비스가 요구하는 형식의 객체타입으로 변환하여 전달) → 응용 서비스 리턴 → (JSON 형식으로 변환하여 HTTP 응답으로 전송
응용 영역 (Service)
- 표현 영역을 통해 사용자의 요청을 전달받는 응용 영역은 시스템이 사용자에게 제공해야 할 기능을 구현
- e.g 주문 등록, 주문 취소, 상품 상세 조회
- 기능을 구현하기 위해 도메인 영역의 도메인 모델을 사용
- 주문 취소 기능을 제공하는 응용 서비스 → 주문 도메인 모델을 사용해서 기능을 구현
- 로직을 직접 수행하기보다는 도메인 모델에 로직 수행을 위임
도메인 영역 (Domain)
- 도메인 모델을 구현
- 도메인의 핵심 로직을 구현
- e.g 배송지 변경, 결제 완료, 주문 총액 계산
인프라스트럭처 영역 (Repository)
- 구현 기술에 대한 것을 다룸
- RDBMS 연동을 처리하고, 메시징 큐에 메시지를 전송하거나 수신하는 기능을 구현하고, 몽고DB나 레디스와 의 데이터 연동 ....
- 논리적인 개념을 표현하기보다는 실제 구현을 다룸
2.2 계층 구조 아키텍처
표현 → 응용 → 도메인 → 인프라스트럭처
- 인프라스트럭처의 기술에 의존하면 '테스트 어려움'과 '기능 확장의 어려움'이라는 두 가지 문제가 발생
2.3 DIP
가격 할인 계산 CalculateDiscountService [고수준]
고객 정보를 구한다 → RDMBS에서 JPA로 구한다 [저수준]
룰을 이용해서 할인 금액을 구한다 → Drools로 룰을 적용한다 [저수준]
- 고수준 모듈 : 의미 있는 단일 기능을 제공하는 모듈
- 고수준 모듈의 기능을 구현하려면 여러 하위 기능이 필요
- 저수준 모듈 : 하위 기능을 실제로 구현
고수준 모듈이 제대로 동작하려면 저수준 모듈을 사용해야 한다.
→ 구현 변경과 테스트가 어려워짐
→ 저수준 모듈이 고수준 모듈에 의존하도록 바꾸자! (D.I.P)
→ 추상화한 인터페이스 활용
public interface RuleDiscounter {
Money applyRules(Customer customer, List<OrderLine> orderLines);
}
고객 정보와 구매 정보에 룰을 적용해서 할인 금액을 구한다
public class CalculateDiscountService {
private RuleDiscounter ruleDiscounter;
public CalculateDiscountService(RuleDiscounter ruleDiscounter) {
this.ruleDiscounter = ruleDiscounter;
}
// 가격 할인 계산
public Money calculateDiscount(List<OrderLine> orderLines, String customerId) {
// 고객 정보를 구한다
Customer customer = findCustomer(customerId);
// 룰을 이용해서 할인 금액을 구한다
return ruleDiscounter.applyRules(customer, orderLines);
}
}
- Drools에 의존하는 코드가 없이 RuleDiscounter가 룰을 적용한다는 사실만 알 뿐임
- 룰 적용을 구현한 클래스는 RuleDiscounter 인터페이스를 상속받아 구현
public class DroolsRuleDiscounter implements RuleDiscounter{
private KieContainer kieContainer;
public DroolsRuleEngine() {
KieServices ks = KieService.Factory.get();
kContainer = ks.getKieClasspathContainer();
}
@Override
public Money applyRules(Customer customer, List<OrderLine> orderLines) {
KieSession kSession = kContainer.newKieSession("discountSession");
try {
} finally {
}
return money.toImmutableMoney();
}
}
DIP 주의사항
- 단순히 인터페이스와 구현 클래스를 분리하는 정도가 아님
- DIP의 핵심은 고수ㅜㄴ 모듈이 저수준 모듈에 의존하지 안호돍 하기 위함
2.4 도메인 영역의 주요 구성 요소
- 엔티티
- 고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 갖는다.
- 주문, 회원, 상품과 같이 도메인의 고유한 개념을 표현한다.
- 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공한다.
- 밸류
- 고유의 식별자를 갖지 않는 객체로 주로 개념적으로 하나인 값을 표현할 때 사용된다.
- 배송지 주소를 표현하기 위한 주소나 구매 금액을 위한 금액과 같은 타입이 밸류 타입이다.
- 앤티티의 속성으로 사용할 뿐만 아니라 다른 밸류 타입의 속성으로도 사용할 수 있다.
- 애그리거트
- 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것이다.
- 주문과 관련된 Order 엔티티, OrderLine 밸류, Orderer 밸류 객체를 주문 애그리거트로 묶을 수 있따.
- 리포지터리
- 도메인 모델의 영속성을 처리한다.
- 도메인 서비스
- 특정 엔티티에 속하지 않은 도메인 로직을 제공한다.
2.4.1 엔티티와 밸류
실제 도메인 모델의 엔티티와 DB 관계형 모델의 엔티티는 다르다
- 도메인 모델의 엔티티
- 데이터와 함께 도메인 기능을 함께 제공한다는 점이다
- 단순히 데이터를 담고 있는 데이터 구조라기보다는 데이터와 함께 기능을 제공하는 객체
- 도메인 관점에서 기능을 구현하고 기능 구현을 캡슐화해서 데이터가 임의로 변경 되는 것을 막음
- 두 개 이상의 데이터가 개념적으로 하나인 경우 밸류 타입을 이용해서 표현할 수 있음
2.4.2 애그리거트
- 관련 객체를 하나로 묶은 군집
- e.g 주문 -> 주문, 배송지 정보, 주문자, 주문 목록, 총 결제 금액
- 애그리거트는 군집에 속한 객체를 관리하는 루트 엔티티를 가짐
- 루트 엔티티는 애그리거트에 속해 있는 엔티티와 밸류 객체를 이용해서 애그리거트가 구현해야 할 기능을 제공
- 애그리거트를 사용하는 코드는 애그리거트 루트가 제공하는 기능을 실행하고 애그리거트 루트를 통해서 간접적으로 애그리거트 내의 다른 엔티티나 밸류 객체에 접근
- e. g 애그리거트 루트 Order
2.4.3 리포지터리
- 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의
'기술 서적 정리 > DDD Start' 카테고리의 다른 글
[도메인 주도 개발 시작하기] 6. 응용 서비스와 표현 영역 (0) | 2022.09.05 |
---|---|
[도메인 주도 개발 시작하기] 5. 스프링 데이터 JPA를 이용한 조회 기능 (0) | 2022.09.05 |
[도메인 주도 개발 시작하기] 4. 리포지터리와 모델 구현 (0) | 2022.09.05 |
[도메인 주도 개발 시작하기] 3. 애그리거트 (0) | 2022.09.05 |
[도메인 주도 개발 시작하기] 1. 도메인 모델 시작 (0) | 2022.09.05 |
댓글