아키텍처는 크게 4개의 영역으로 구분되는 계층구조로 이루어졌다.
o 표현계층: 사용자의 요청을 응용 영역에 전달, 응용 영역의 처리 결과를 다시 사용자에게 보여주는 역할. Json <--변환--> Object)
o 응용계층: 사용자에게 제공할 기능 구현. 직접 구현보다는 도메인 모델에 로직 수행을 위임.
o 도메인계층: 도메인의 핵심 로직을 구현. ex. 주문 도메인의 '결제 완료', '주문 총액 계산', '배송지 변경' 같은 로직
o 인프라스트럭쳐계층: 상세한 구현 기술을 다룸. ex. DB연동, HTTP Client, Kafka ...
위 아키텍처는 상위 -> 하위계층으로만 의존을 하도록 되어있는데, 이 경우에 두가지 문제점이 발생한다.
(1) 테스트의 어려움 - 상위 계층을 테스트하기 위해서는 하위 계층이 구현되어있어야한다.
(2) 기능 확장의 어려움 - 하위 계층에 수정사항이 생기면 이를 의존하고 있는 상위계층까지 수정해야한다.
의존역전원칙 (DIP: Dependency Inversion Principle)
: 위의 문제를 해결하기 위해서, 저수준 모듈이 고수준모듈에 의존하도록 바꾸는 것을 말한다.
고수준모듈(Service)에서는 어떤 구체적인 기술들로 저수준 모듈을 구현했는지 그닥 중요하지 않다. 오히려 저수준 모듈이 수정되더라도 영향을 받지 않도록 구현부분에 의존하지 않아야한다. ==> 추상화 인터페이스
인터페이스를 사용하면 아까 말한 두가지의 문제점은 다음과 같이 해결된다.
(1) 테스트의 어려움 -> Mock, Stub같은 대용객체를 생성하여 고수준모듈에서 사용
(2) 기능 확장의 어려움 -> 저수준모듈을 고수준모듈에 의존주입(DI)
예를 들어보자.
"가격 할인 계산" 이라는 기능을 위해서는 고객 정보를 가져오고, 고객에 맞는 룰을 적용해야 한다는 하위 기능을 구현해야한다. 이 때, 구현하고자 하는 하위 기능이 고수준 모듈, 실제 하위 기능을 구현한 부분이 저수준 모듈이다.
추상화한 인터페이스를 통해 저수준모듈을 주입받음으로써 고수준모듈은 더이상 구현기술에 의존하지 않는다.
[DIP 적용 구현코드]
- CalculateDiscountService.java
public class CalculateDiscountService {
private CustomerRepository customerRepo;
private RuleDiscounter ruleDiscounter;
// 서비스에서 사용할 저수준모듈(로직구현클래스) DI
// 추상화 Interface사용으로 의존 X
public CalculateDiscountService(CustomerRepository customerRepo, RuleDiscounter ruleDiscounter) {
this.customerRepo = customerRepo;
this.ruleDiscounter = ruleDiscounter;
}
// 가격할인계산 <= 고객정보를 구한다. 룰을 이용해서 할인 금액을 구한다.
public Money calculateDiscount(List<OrderLine> orderLines, String customerId) {
Customer customer = customerRepo.findCustomer(customerId);
return ruleDiscounter.applyRules(customer, orderLines);
}
}
- RuleDiscounter.java
: 서비스의 하위기능 수행을 위한 인터페이스
public interface RuleDiscounter {
public Money applyRules(Customer customer, List<OrderLine> orderLines);
}
- DroolsDiscounter.java / ABCRuleDiscounter.java
: 인터페이스를 구현한 부분. Service에서 목적에 맞게 주입하여 사용.
public class DroolsDiscounter implements RuleDiscounter {
// 생성자부분 생략
@Override
public Money applyRules(Customer customer, List<OrderLine> orderLine) {
Money discountedMoney = new Money();
// Drools로 가격할인 계산 구현부분.
// 로직 생략..
return discountedMoney;
}
}
public class ABCRuleDiscounter implements RuleDiscounter {
// 생성자부분 생략
@Override
public Money applyRules(Customer customer, List<OrderLine> orderLine) {
Money discountedMoney = new Money();
// ABC 자체로직으로 가격할인 계산 구현부분.
// 로직 생략..
return discountedMoney;
}
}
(CustomerRepository은 RuleDiscounter 부분과 비슷하여 생략한다.)
[패키지 구조]
[참고서적] DDD START! 도메인 주도설계 구현과 핵심 개념 익히기, 최범균 저자
'책책책' 카테고리의 다른 글
[오브젝트] 객체지향 프로그래밍의 역할, 책임, 협력 (0) | 2022.02.26 |
---|---|
[DDD START!] 애그리거트를 통한 복잡한 도메인 모델 간 관계 관리 (0) | 2021.08.20 |