반응형
Chapter 2
2.1 네 개의 영역
표현 영역
- HTTP 요청을 응용 영역이 필요로 하는 형식으로 변환하여 응용 영역에 전달.
- 응용 영역의 응답을 HTTP로 변환하여 전달.
응용 영역
- 사용자에게 제공해야할 기능을 구현.
public class CancelOrderService {
@Transactional
public void cancelOrder(String orderId){
Order order = findOrderById(orderId);
if(order == null) throw new OrderNotFoundException(orderId);
order.cancel();
}
}
- 위의 예시처럼 응용 서비스는 로직을 직접 수행하는 것이 아니라 도메인 모델에 로직 수행을 위임한다.
도메인 영역
- 도메인 모델을 구현한다.
- 도메인의 핵심 로직을 구현한다.
인프라스트럭처 영역
- 구현 기술에 대한 기능을 개발한다.
- RDBMS 연동 처리한다.
2.2 계층 구조 아키텍처
- 계층 구조는 상위 계층에서 하위 계층으로의 의존만 존재한다.
계층 종속시 발생하는 문제점
import java.util.Arrays;
public class CalculateDiscountService {
private DroolsRuleEngine ruleEngine;
public CalculateDiscountService(){
ruleEngine = new DroolsRuleEngine();
}
public Money calculateDiscount(List<OrderLine> orderLines, String customerId){
Customer customer = findCustomer(customerId);
MutableMoney money = new MutableMoney(0);
List<?> facts = Arrays.asList(customer, money);
facts.addAll(orderLines);
ruleEngine.evalute("discountCalculation", facts);
return money.toImmutableMoney();
}
}
- CalculateDiscountService만 테스트 하기 어렵다.
- 구현 방식 변경이 어렵다.
- 특정 Entity나 Type Value와 관련된 기능 수정이 매우 어렵다.
DIP
고수준 모듈과 저수준 모듈
- 고수준 모듈: 의미 있는 단일 기능을 제공하는 모듈
- CalculateDiscountService는 가격 할인 계산 이라는 기능을 구현한다.
- 저수준 모듈: 고수준 모듈을 위한 하위 기능을 구현한다.
- JPA를 사용하여 고객 정보를 읽어오는 모듈, Drools로 룰을 실행하는 모듈.
- 고수준 모듈이 저수준 모듈을 사용하면 앞서 말한 두개의 문제가 발생한다.
인터페이스의 활용
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);
}
}
- CalculateDiscountService에는 Drools에 의존하는 코드가 없다.
- 의존하지 않음
public class DroolsRuleDiscounter implements RuleDiscounter{
private KieContainer kContainer;
public DroolsRuleDiscounter(){
KieService ks = KieService.Factory.get();
kContainer = ks.getKieClasspathContainer();
}
@Override
public Money applyRules(Customer customer, List<OrderLine> orderLines){
}
}
- 이렇게 저수준 모듈로 고수준 모듈로 구현한다.
- 이렇게 되면 위와 같은 아래와 같은 구조를 가진다.
- 위처럼 고수준 모듈을 인터페이스로 정의하고, 저수준 모듈을 해당 인터페이스의 구현체로 개발하면 저수준 모듈이 고수준 모듈에 의존하게 된다.
- DIP는 해당 현상처럼 의존성이 반전되어 Dependency Inversion Principle 이라 불리는 것이다.
- 사용할 저수준 객체만을 변경하면 수정사항은 객체 생성 코드밖에 되지 않는다.
- 테스트 코드가 쉽다.
DIP의 주의 사항
- DIP를 인터페이스와 구현 클래스로 분리하는 정도로 받아들이면 안된다. (뜨끔❗)
- DIP의 핵심은 고수준 모듈이 저수준 모듈에 의존하지 않도록 하기 위함이다.
- 관점을 무조건 고수준 모듈에 두어야 한다.
- ex) CalculateDiscountService의 입장에서 할인 금액을 구하기 위한 엔진 기술은 생각할 필요 없다. 단지 어떤 값이 들어가고 어떤 값이 도출 되는 지가 중요하다.
DIP와 아키텍쳐
- DIP를 적용한 인프라 구조에서는 인프라 스트럭처 구현체만 변경한다면 고수준 인터페이스는 변경할 필요가 없다.
도메인 영역의 주요 구성요소
도메인 영역의 구성요소
- Entity: 고유의 식별자를 가지는 객체, 고유의 도메인 개념을 표현한다.
- Value: 고유의 식별자를 갖지 않는 객체, 개념적으로 하나인 값을 표현할 때 사용된다. 배송지 주소를 표현하기 위한 주소나,금액이 이에 해당한다.
- Aggregate: 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것이다.
- Order entity, OrderLine value, Order value 객체들을 하나의 ‘주문’ aggregate로 묶을 수 있다.
- Domain service: 특정 엔티티에 속하지 않는 도메인 로직.
- 할인 금액 계산이라는 기능은 상품, 쿠폰, 회원 등급, 구매 금액 등 다양한 조건을 이용해서 구현하게 되는데, 이렇게 도메인 로직이 여러 Entity와 Value를 필요로 하면 도메인 서비스에서 해당 로직을 구현한다.ㅇ
Entity와 Value
Domain Entity와 DB Entity의 차이점
- 도메인은 메소드까지 제공한다.
- 도메인은 Value type을 사용해 종속 개념을 명확하게 표현할 수 있다.
애그리거트
- 애그리거트: 관련 객체를 하나로 묶은 군집
- 도메인 모델이 복잡해지면 전체적인 구조를 파악하기 힘들기에 이 부분을 해결하는데 유용하다.
Root Entity
- 군집에 속한 객체를 관리하는 엔티티.
- 애그리거트에 속해 있는 엔티티와 밸류 객체를 이용해 기능을 제공한다.
- 각 애그리거트의 모든 기능은 Root Entity를 통해 제공된다. 따라서 모든 도메인의 기능은 Root Entity의 로직을 거쳐야한다. (이게 진짜 대박이네)
레포지터리 (이부분은 제가 새로 알게 된 부분만을 정리했습니당!)
- Repository의 역할만을 생각했을 때 Infra Structure에 속할 것이라고 생각했다.
- 그러나 해당 Repository를 고수준의 인터페이스로 정의하고 구현체만을 Infra Structure로 취급하는 부분이 매우 인상 깊었다.
- 위의 구조가 좋았던 이유는 말 그대로 Infra Structure만 수정되면 해당 Repository를 사용하는 객체의 큰 변화가 없다는 장점이 있다.
요청 처리 흐름
- 해외 자료에서도 응용계층에서 Infrastructure를 쓰는 구조는 무조건 나타나는군…
- Spring boot의 controller, service, dao, repository의 개념과 비슷하다.
인프라스트럭처 개요
- 도메인 객체의 영속성 처리, 트랜잭션, SMTP 클라이언트, Rest 클라이언트 등의 기능을 구현한다.
- 도메인 영역과 인프라 영역에서 정의한 인터페이스를 인프라스트럭처 영역에서 구현하는 것이 좋다.
- 응용영역에서 인프라스트럭처에 대한 의존성을 무조건 없애야할 필요는 없다.
- Transactional 같은 경우 Spring에서 제공하는 어노테이션을 사용하는 것이 더 편리하다.
- @Entity와 같은 어노테이션을 도메인 모델 클래스에 사용하는 것이 더 편리하다.
- 그럼 어노테이션이 인프라스트럭처 영역인가…?
- DIP의 장점을 해치지 않는 범위에서 응용 영역과 도메인 영역에서 구현 기술에 대한 의존을 가져가는 것도 나쁘지 않다. (상황에 따라)
모듈 구성
- 도메인 별로 UI 계층까지 따로 만든다는 것이 조금 놀랐다.
- 생각해보면 controller단, service단 이런식으로 나누던 것을 각 기능별로 나눈 것이라고 생각되기도 한다.
- 애그리거트 기준으로 패키지를 구현하고 Repository와 같은 인터페이스도 domain 폴더 안에 넣는다는 것 또한 인상 깊었다.
반응형
'programming > DDD' 카테고리의 다른 글
[DDD] chapter 6 (0) | 2024.02.10 |
---|---|
[DDD] chapter 5 (1) | 2024.02.07 |
[DDD] chapter 4 (0) | 2024.02.03 |
[DDD] chapter 3 (0) | 2024.01.18 |
[DDD] chapter 1 (0) | 2024.01.13 |