아키텍처
1. 구성요소 : 표현 | 응용 | 도메인 | 인프라스트럭처
* 고수준 모듈 : 의미있는 단일 기능 (예시 : 고객 정보를 조회 + 할인율 계산)
* 저수준 모듈 : 하위 기능을 실제로 구현한 것 (예시: JPA를 이용한 DB 조회 + Drools을 이용한 룰 적용)
* 인프라스트럭처의 저수준 모듈을 이용하여 고수준 모듈의 기능을 개발
영역 | 설명 | SpringBoot 계층 예시 |
UI 또는 표현 | 사용자의 요청을 처리하고 응답을 보여줌 여기서 사용자는 사람일 수도 있고, 외부 시스템일 수도 있음 |
컨트롤러 (API계층) |
응용(Application) | 사용자가 요청한 기능 실행 도메인 계층을 조합해서 기능 실행 (로직의 직접 구현 X) * 예시1 : Entity에 구현한 메서드를 호출해서 사용 * 예시2 : 레포지토리에 구현한 쿼리메서드를 호출해서 사용 |
서비스 (비즈니스 계층) |
도메인 | 도메일 모델 구현 로직의 수행은 도메인 모델에 위임 도메인 모델 : 시스템이 제공할 도메인의 핵심 로직을 구현 |
레포지토리 (고수준 모듈) 도메인 모델 = 엔티티 메서드 등 |
인프라 스트럭처 | 논리적 개념의 표현보단, 실제 구현 1. RDBMS와의 연동 2. 메세징 큐에 메세지를 전송 3. 몽고DB나 Redis와의 데이터 연동 4. SMTP를 이용한 매일 발송 5. HTTP를 이용한 REST API 호출 |
JpaOrderRepository, Redis, EmailNotifier, CompositeNotifier, MyBatisOrderRepository, DroolsRuleDiscounter, 등 (저수준 모듈) |
2. 계층구조 : 일반적으로 4단계 계층구조에 따라 바로 아래 계층에 의존하지만, 효율에 따라 인프라스트럭처에 바로 의존하는 경우가 있음.
ex) Transaction 등
DIP (= 의존성 주입, DI, Dependency Injection)
1. 고수준모듈이 저수준모듈에 의존하면 구현 변경과 테스트가 어려워짐
=> 저수준모듈이 고수준모듈에 의존하도록 하기 위해 중간에 Interface로 추상화하여 연결 (= DI)
의존성 주입(DI) 방법 코드
public class TemplateService {
// 필드 선언 후 생성자를 이용한 할당
private TemplateRepository templateRepository;
public TemplateService (TemplateRepository templateRepository) {
this.templateRepository = templateRepository;
}
// 실제 사용예 : templateRepository에서 특정 계산 메서드(caculate(int))를 호출해서 사용
public Money calculateDiscount (List<Money> moneyList) {
int count = moneyList.size();
int result = 0;
while(count-->0) {
result += templateRepository.calculate(moneyList.getMoney(count));
}
return result;
}
...
}
2. 테스트(@Test)시 대역객체를 사용해서 테스트 진행이 가능
public class CalcuateDiscountServiceTest {
@Test
public void noCustomer_tenExceptionShouldBeThrown() {
// 테스트 목적의 대역 객체
CustomerRepository stubRepo = mock(CustomerRepository.class);
when(stubRepo.findById("noCustId").thenReturn(null);
RuleDiscounter stubRule = (cust, lines) -> null;
// 대용객체를 주입 받아 테스트 진행
CalculateDiscountService calDisSvc =
new CalculateDiscountService(stubRepo, stubRule);
assertThrows(NoCustomerException.class,
() -> calDisSvc.calculateDiscount(someLines, "noCustId"));
}
}
DIP 구조 예시

도메인 영역의 주요 구성요소
1. 도메인 모델 : 도메인의 주요 개념을 표현하며 핵심로직을 구현
2. 구성요소
요 소 | 설 명 |
엔티티 (Entity) |
: 고유 식별자를 갖는 객체 : 도메인의 고유한 개념을 표현 : 도메인 모델의 데이터(필드)를 포함하며, 해당 데이터와 관련된 기능(메서드)을 함께 제공 ex) Order 1 = id가 1인 값을 가진 Order |
밸류 (Value) |
: 고유 식별자를 갖지 않는 객체 : 개념적으로 하나의 값을 표현 : 엔티티의 속성으로 사용하거나, 다른 밸류 타입의 속성으로도 사용 ex) Orderer = 하나의 Order에 대한 Orderer값 |
애그리거트 (Aggrigate) |
: 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것 ex) Order 애그리거트 = Order 엔티티 + OrderLine 밸류+ Orderer 밸류 |
레포지토리 (Repository) |
: 도메인 모델의 영속성을 처리 ex) RDBMS 테이블에서 엔티티 객체를 로딩하거나 저장하는 기능을 제공 |
도메인 서비스 (Domain Service) |
: 도메인 로직이 여러 엔티티와 밸류를 필요로 하면 도메인 서비스에서 로직을 구현 : 특정 엔티티에 속하지 않은 도메인 로직을 제공 ex) 할인금액계산 = 상품+쿠폰 +회원등급+구매금액 등 다양한 도메인의 조건을 사용해야함 |
3. 실제 도메인 모델의 엔티티와 DB의 관계형 모델(RDBMS)의 엔티티의 차이
1) 도메인 모델의 엔티티는 데이터(필드)와 함께 도메인 기능(메서드)을 제공함
2) 도메인 모델의 엔티티는 두 개 이상의 데이터가 개념적으로 하나인 경우, 밸류 타입을 이용해서 표현가능(인스턴스생성)
그러나, RDBMS에서는 다른 테이블에 정보를 넣을 때, 연관된 다른 테이블을 하위 개념으로 잡음
4. 애그리거트
: 관련 객체를 하나로 묶은 군집 (애그리거트 단위로 캡슐화)
: 도메인 모델에서 전체 구조를 이해하는데 도움을 줌
: 루트 엔티티는 애그리거트애 속해 있는 엔티티와 밸류 객체를 이용해서 애그리거트가 구현해야 할 기능을 제공
5. 레포지토리
: 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의
인프라스트럭처
: 표현 | 응용 | 도메인 영역을 지원
: 도메인 객체의 영속성 처리, 트랜잭션, SMTP 클라이언트, REST 클라이언트 등 다른 영역에서 필요로 하는 프레임워크, 구현 기술, 보조 기능을 지원
: DIP의 장점을 해치지 않는 범위에서 응용 영역과 도메인 영역에서 구현 기술에 대한 의존을 가져가는 것도 좋다
ex) @Transactional @Entity @Table 등
모듈
1. 구성
1) 계층별 : 표현 | 응용 | 도메인 | 인프라스트럭처로 패키지 구분
2) 기능별 : 각 하위 도메인에 속한 애그리거트별 패키지 구분(각 패키지마다 내부적으로 계층별 구분함)
'독서 > DDD' 카테고리의 다른 글
[DDD] Chapter 4. 리포지터리와 모델 구현 (0) | 2023.06.04 |
---|---|
[DDD] Chapter 3. 애그리거트 [3/11] (0) | 2023.01.13 |
[DDD] Chapter 1. 도메인 모델 시작 [1/11] (0) | 2023.01.03 |