표현 영역은 사용자의 요청을 해석해서 응용 서비스에 전달하고 응용 서비스의 실행 결과 를 사용자가 이해할 수 있는 형식으로 변환해서 응답한다.
웹 애플리케이션에서 표현 영역은 HTTP 요청을 응용 영역이 필요로 하는 형식으로 변환해서 응용 영역에 전달하고, 응용 영역의 응답을 HTTP 응답으로 변환해서 전송한다.
표현 영역을 통해 사용자의 요청을 전달받는 응용 영역은 시스템이 사용자에게 제공해야할 기능을 구현한다.
응용 영역은 기능을 구현하기 위해 도메인 영역의 도메인 모델을 사용한다.
응용 서비스는 로직을 직접 수행하기보다는 도메인 모델에 로직 수행을 위임한다.
인프라스트럭처 영역은 구현 기술에 대한 것을 다룬다.
인프라스트럭처 영역은 논리적인 개념을 표현하기보다는 실제 구현을 다룬다.
도메인 영역, 응용 영역, 표현 영역은 구현 기술을 사용한 코드를 직접 구현하진 않는다.
인프라스트럭처 영역에서 제공하는 기능을 사용, 필요한 기능을 개발한다. (DB 모듈 사용한 데이터 조회, SMTP 연동 모듈을 이용한 메일 전송 등)
계층 구조의 아키텍처 구성
구현의 편리함을 위해 계층 구조를 유연하게 적용할 수 있다.
고수준 모듈과 저수준 모듈
DIP는 이 문제를 해결하기 위해 저수준 모듈이 고수준 모듈에 의존하도록 바꾼다.
public interface RuleDiscounter {
public 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);
}
}
추상화한 인터페이스를 활용.
다른 버전의 룰 적용 객체 생성 시 인터페이스를 구현(implements)하여 생성
DIP를 적용하면 저수준 모듈이 고수준 모듈에 의존하게 됨.
DIP를 잘못 생각하면 단순히 인터페이스와 구현 클래스를 분리하는 정도로 받아들일 수 있다.
DIP의 핵심은 고수준 모듈이 저수준 모듈에 의존하지 않도록 하기 위함
DIP 적용한 결과 구조만 보고 저수준 모듈에서 인터페이스를 추출하는 경우가 있음.
도메인 영역이 구현 기술을 다루는 인프라스트럭처 영역에 의존하는 형태
즉, '할인 금액 계산' 을 추상화한 인터페이스는 저수준 모듈이 아닌 고수준 모듈에 위치해야 한다.
아키텍처 수준에서 DIP를 적용하면 인프라 영역이 응용 영역과 도메인 영역에 의존하는 구조가 된다.
entity와 value 개수가 많아지만 모델은 점점 복잡해진다.
도메인 모델에서 전체 구조를 이해하는 데 도움이 되는 것이 바로 Aggregate (에그리거트)
Aggregate는 군집에 속한 객체들을 관리하는 루트 엔티티를 갖는다.
에그리거트에 속해 있는 엔티티와 벨류 객체를 이용, 구현해야 할 기능을 제공한다.
에그리거트 구현 시에는 고려할 부분이 많음.
도메인 객체를 지속적으로 사용하려면 RDBMS, NoSQL, 로컬 파일 등의 물리적 저장소에 도메인 객체를 보관해야 한다.
리포지터리는 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의한다.
예를 들어, 주문 애그리거트를 위한 리포지터 리는 다음과 같이 정의할 수 있다.
public interface OrderRepository {
public Order findByNumber(OrderNumber number);
public void save(Order order);
public void delete(Order order);
}
응용 서비스와 리포지터리는 밀접한 연관이 있다.
리포지터리의 사용 주체가 응용 서비스이기 때문에 리포지터리는 응용 서비스가 필요로 하는 메서드를 제공한다.
infrastructure는 표현 영역, 응용 영역, 도메인 영역을 지원한다.
DIP를 통해 infrastructure 기능을 직접 사용하는 것 보다 추상화된 인터페이스의 구현 객체를 활용하는 것이 시스템을 더 유연하고 테스트하기 쉽게 만들어준다.
다만, 무조건 infrastructure에 대한 의존을 없애는 것은 좋은 것이 아니다.
예를 들어, 스프링을 사용할 경우 응용 서비스는 트랜잭선 처리를 위해 스프링이 제공하는 @Transactional을 사용하는 것이 편리하다.
영속성 처리를 위해 JPA를 사용할 경우 @Entity 나 @Table 과 같은 JPA 전용 애노테이션(annotation)을 도메인 모델 클래스 에 사용하는 것이 XML 매핑 설정을 이용하는 것보다 편리하다.
// 구현의 편리함을 위해 인프라스트릭처에 대한 의존을 일부 도메인에 넣은 코드.
// JPA의 @Table annotation을 이용해서 엔티티를 저장할 테이블 이름을 지정.
// XML 설정을 사용하는 것보다 펀리하게 테이블 이름을 지정할 수 있다.
@Entity
@Table(name = "TBL_ORDER")
public class Order {
}
구현의 편리함은 DIP가 주는 다른 장점(변경의 유연함, 테스트가 쉬움)만큼 중요하기 때문에 DIP의 장점을 해치지 않는 범위에서 응용 영역과 도메인 영역에서 구현 기술에 대한 의존을 가져가는 것이 현명하다.
응용 영역과 도메인 영역이 인프라스트럭처 에 대한 의존을 완전히 갖지 않도록 시도하는 것은 자칫 구현을 더 복잡하고 어렵게 만들 수 있다.
표현 영역 - 인프라스트럭처 영역