2.1.1 표현
2.1.2 응용
2.1.3 도메인
2.1.4 인프라스트럭처
구현 기술에 대한 것을 다룸
구현 기술
도메인, 응용, 표현 영역은 구현 기술을 사용한 코드를 만들지 않고, 인프라스트럭처 영역에서 제공하는 기능을 사용해서 필요한 기능을 개발함
고수준 모델과 저수준 모델
고수준 모듈이 제대로 동작하기 위해 저수준 모듈을 사용해야 하는데, 이때 구변 변경과 테스트가 어렵다는 문제가 발생함.
➡️ DIP는 이 문제를 해결하기 위해 추상화한 인터페이스를 이용하여 저수준 모듈이 고수준 모듈에 의존하도록 바꿈.
// 소스코드에도 어디에 있는지 모르겠어서... 일단 여기에 작성
public class DroolsRuleDiscounter implements RuleDiscounter {
private KieContainer kContainer;
public DroolsRuleEngine() {
KieServices ks = KieServices.Factory.get();
kContainer = ks.getKieClasspathContainer();
@Override
public Money applyRules(Customer customer, List<OrderLine> orderLines) {
KieSession kSession = kContainer.newKieSession("discountSession");
try {
...
kSession.fireAllRules();
} finally {
kSession.dispose();
}
return money.toImmutableMoney();
}
}
즉, 저수준 모듈이 고수준 모듈에 의존하는 것을 DIP, Dependency Inversion Principle이라고 함.
// 사용할 저수준 객체 생성
RuleDiscounter ruleDiscounter = new DroolsRuleDiscounter();
// 생성자 방식으로 주입
CalculateDiscountService disService = new CalculateDiscountService(ruleDiscounter);
// 구현 기술 변경
// 사용할 저수준 구현 객체 변경
RuleDiscounter ruleDiscounter = new SimpleRuleDiscounter();
// 사용할 저수준 모듈을 변경해도 고수준 모듈을 수정할 필요가 없음
CalculateDiscountService disService = new CalculateDiscountService(ruleDiscounter);
2.3.1 DIP 주의사항
2.3.2 DIP와 아키텍쳐
2.4.1 엔티티와 밸류
2.4.2 애그리거트
2.4.3 리포지터리
리포지터리: RDBMS, NoSQL, 로컬 파일과 같이 도메인 객체를 지속적으로 보관, 사용하기 위해 이용하는 도메인 모델.
애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의함.
ex.
public interface OrderRepository {
// 대상을 찾고 저장하는 단위가 애그리거트 루트인 Order임.
// 애그리거트에 속한 모든 객체를 포함하고 있기 때문.
Order findByNumber(OrderNumber number);
void save(Order order);
void delete(Order order);
➡️ 도메인 객체를 영속화하는데 필요한 기능을 추상화한 것으로 고수준 모듈에 속하며, 실제 구현 클래스틑 인프라스트럭처 영역에 속함
응용 서비스와 리포지터리의 연관성
리포지토리가 제공하는 메서드
public interface SomeRepository {
void save(Some some);
Some findById(SomeId id);
리포지터리를 구현하는 방법은 선택한 구현 기술에 따라 달라짐.
도메인 모델을 이용하여 기능을 구현.
기능 구현에 필요한 도메인 객체를 리포지터리에서 가져와 실행하거나 신규 도메인 객체를 생성해서 리포지토리에 저장. 혹은 두 개 이상의 도메인 객체를 사용하여 구현하기도 함.
ex.
public class CancelOrderService {
private OrderRepository orderRepository;
@Transactional // 응용 서비스는 트랜잭션을 관리
public void cancel(OrderNumber number) {
Order order = orderRepository.findByNumber(number);
if (order == null) throw new NoOrderException(number);
order.cancel();
}
}
표현 영역, 응용 영역, 도메인 영역을 지원
= 도메인 객체의 영속성 처리, 트랜잭션, SMTP 클라이언트, REST 클라이언트 등 다른 영역에서 필요로 하는 프레임워크, 구현 기술, 보조 기능을 지원
도메인 영역과 응용 영역에서 정의한 인터페이스를 인프라스트럭처 영역에서 구현하는 것(=인프라에 대한 의존성을 없애는 것, DIP)이 시스템을 더 유연하고 테스트하기 쉽게 만들어줌.
하지만 무조건 인프라스트럭처에 대한 의존을 없앨 필요는 없음. 구현의 편리함을 위해 DIP의 장점을 해치지 않는 범위에서 의존을 가져가는 것도 괜찮고, 완전히 의존을 없애고자 시도하는 것은 자칫 구현을 더 복잡하고 어렵게 만들 수 있기 때문.
ex. @Transactional 애너테이션 ➡️ 이 애너테이션을 사용하지 않으려면 스프링에 대한 의존을 없애기 위해 복잡한 스프링 설정을 사용해야 함.
도메인에 속한 애그리거트를 기준으로 다시 패키지를 구성
모듈 구조를 얼마나 세분화해야 하는지 정해진 규칙은 없지만, 한 패키지에 너무 많은 타입이 몰려 코드를 찾을 때 불편하지 않도록 가능한 10~15개 미만으로 타입 개수를 유지하기