티스토리 뷰
728x90
반응형
네 개의 영역
💡 표현 영역의 역할
- 사용자의 요청을 받아 응용 영역이 필요로 하는 데이터 형식으로 변환해서 응용 영역에 전달하며, 응용 영역의 처리 결과를 다시 사용자에게 보여주는 역할을 합니다.
💡 응용 영역의 역할
- 사용자에게 제공해야할 기능을 구현하며, 기능을 구현하기 위해 도메인 영역의 도메인 모델에 로직 수행의 책임을 위임합니다.
💡 도메인 영역의 역할
- 도메인 모델은 도메인의 핵심 로직을 구현합니다.
💡 인프라스트럭처 영역의 역할
- 데이터베이스와 연동을 처리하거나 MQ를 사용하여 메시지 처리 등 논리적인 개념을 표현하기보다 실제 구현을 다룹니다.
계층 구조 아키텍처
서비스 레이어 아키텍처의 구조는 상위 계층은 하위 계층을 의존하되 하위 계층은 상위 계층을 의존해서는 안된다는 것입니다.
DIP : 의존성 역전 원칙
의존성 역전 원칙이란 고수준과 저수준 모듈은 모두 추상화에 의존하여 유연한 설계가 가능하도록 만들기 위한 원칙입니다.
💡 예제 코드
- Movie 객체는 기간 할인 정책을 통해 가격을 계산합니다. 이때 Movie 클래스를 테스트 하기 위해서는 사전에 기간 할인 정책이 세팅되어 있어야합니다, 또한 기간 할인 정책을 다른 정책으로 변경할 때 문제가 발생하게 됩니다. 이렇게 2가지 문제가 발생하게 됩니다.
@AllArgsConstructor
public class Movie {
private String title;
private LocalDate whenScreened;
private PeriodDiscountPolicy periodDiscountPolicy;
public int getPriceDiscountPolicy() {
return periodDiscountPolicy.calculateDiscount(whenScreened);
}
}
@AllArgsConstructor
public class PeriodDiscountPolicy {
private LocalDate startDate;
private LocalDate endDate;
public int calculateDiscount(LocalDate whenScreened) {
if (whenScreened.isAfter(startDate) && whenScreened.isBefore(endDate)) {
// 로직 구현
}
return 0;
}
}
public class Main {
public static void main(String[] args) {
PeriodDiscountPolicy periodDiscountPolicy = new PeriodDiscountPolicy(LocalDate.of(2022, 12, 24), LocalDate.of(2022, 12, 26));
Movie movie = new Movie("나 홀로 집에", LocalDate.of(2022, 12 ,25), periodDiscountPolicy);
}
}
💡 DIP를 적용한 해결
@AllArgsConstructor
public class Movie {
private String title;
private LocalDate whenScreened;
private DiscountPolicy discountPolicy;
public int getPriceDiscountPolicy() {
return discountPolicy.calculateDiscount(whenScreened);
}
}
@FunctionalInterface
public interface DiscountPolicy {
int calculateDiscount(LocalDate whenScreened);
}
public class PercentDiscountPolicy implements DiscountPolicy {
@Override
public int calculateDiscount(LocalDate whenScreened) {
// 로직 구현
return 0;
}
}
@AllArgsConstructor
public class PeriodDiscountPolicy implements DiscountPolicy {
private LocalDate startDate;
private LocalDate endDate;
@Override
public int calculateDiscount(LocalDate whenScreened) {
if (whenScreened.isAfter(startDate) && whenScreened.isBefore(endDate)) {
// 로직 구현
}
return 0;
}
}
public class Main {
public static void main(String[] args) {
Movie movie = new Movie("나 홀로 집에", LocalDate.of(2022, 12 ,25), new PercentDiscountPolicy());
}
}
💡 DIP를 적용한 구조
- 고수준 모듈이 저수준 모듈을 사용하기 위해서는 고수준 모듈이 저수준 모듈에 의존해야하는데, 반대로 저수준 모듈이 고수준 모듈에 의존한다고 해서 이를 DIP : 의존성 역전 원칙이라 합니다.
💡 DIP 주의 사항
- DIP를 잘못 생각하면 단순히 인터페이스와 구현 클래스를 분리하기 위함 정도로 받아들일 수 있습니다. DIP의 핵심은 고수준 모듈이 저수준 모듈에 의존하지 않도록 하기 위함인데 DIP를 적용한 결과 구조만 보고 아래처럼 저수준 모듈에서 인터페이스를 추출하는 경우가 있습니다.
- DIP를 적용할 때 하위 기능을 추상화한 인터페이스는 저수준 모듈의 관점이 아닌 고수준 모듈의 관점에서 추출해야 합니다. Movie 클래스에서 할인 정책을 통해 가격을 구하는데 이때 기간 할인인지, 비율 할인인지는 중요하지 않습니다. 그저 할인 정책을 통해 가격을 구한다라는 것이 중요합니다. 그렇기 때문에 저수준의 관점이 아닌 고수준의 관점에서 퍼블릭 인터페이스를 추출해야 합니다.
도메인 영역의 주요 구성요소
요소 | 설명 |
엔티티 | 고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 갖습니다. Order, Product, Member등과 같이 표현할 수 잇으며 데이터를 포함하며 해당 데이터와 관련된 기능을 제공합니다. |
밸류 | 고유의 식별자를 갖지 않는 객체로 주로 개념적으로 하나인 값을 표현할 때 사용합니다. Address, Money |
애그리게이트 | 에그리게이트는 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것입니다. 예를들어 주문과 관련된 Order, OrderList 밸류, Orderer 밸류 객체를 주문 애그리게이트로 묶을 수 있습니다. |
리포지터리 | 도메인 모델의 영속성을 처리합니다. |
도메인 서비스 | 특정 엔티티에 속하지 않은 도메인 로직을 제공합니다. |
💡 엔티티와 밸류
- 엔티티와 밸류의 가장 큰 차이점은 엔티티는 데이터와 함께 도메인 기능을 제공합니다. 도메인 모델의 엔티티는 단순히 데이터를 담고 있는 데이터 구조가 아닌 데이터와 함께 기능을 제공하는 객체입니다.
- 엔티티는 두 개 이상의 데이터가 개념적으로 하나인 경우 밸류 타입을 이용해서 표현할 수 있습니다.
💡 애그리게이트
- 애그리게이트는 관련 객체를 하나로 묶은 군집입니다. 예를들어 주문 애그리게이트의 경우 주문, 배송지 정보, 주문자, 주문 목록, 총 결제 금액의 하위 모델로 구성되며 이 하위 모델을 하나로 묶어서 주문이라는 상위 개념으로 표현할 수 있습니다.
- 애그리게이트는 군집에 속한 객체를 관리하는 루트 엔티티를 갖습니다. 루트 엔티티는 애그리게이트에 속해 있는 엔티티와 밸류 객체를 이용해서 애그리게이트가 구현해야 할 기능을 제공합니다.
- 애그리게이트를 사용하는 코드는 애그리게이트 루트가 제공하는 기능을 실행하고 루트 엔티티를 통해 간접적으로 다른 엔티티나 밸류객체에 접근합니다. 이것은 애그리게이트의 내부 구현을 숨겨서 애그리게이트 단위로 구현을 캡슐화할 수 있도록 돕습니다.
- 뭔말인지 모르겠다. 85P
💡 리포지터리
- 도메인 객체를 지속적으로 사용하고 관리하기 위해서는 RDBMS, NoSQL 등과 같은 물리적인 저장소에 도메인 객체를 보관해야하며 이를 위한 도메인 모델이 Repository 입니다.
- Repository는 애그리게이트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의합니다.
✔️ 궁금했던 점
1. DIP의 주의사항 중 추상화를 통해 생성된 인터페이스를 인프라 영역이 아닌 도메인 영역에 위치해야한다고 하는데 고수준 모듈에 위치해야 하는 이유는 무엇인지 모르겠습니다. 제가 생각하기로는 추상화를 통해 접근하기만 하면 되지 않을까? 위치까지 고려해야하나? 라는 의문이 듭니다.
- 배포 진행 과정에서 독립적으로 배포가 불가능하게 되므로 추상화된 인터페이스는 도메인 영역에 존재하는게 유리합니다.
https://koseungbin.gitbook.io/wiki/books/undefined/part-2.-di/solid/dependency-inversion-principle
2. 애그리게이트를 어떻게 나눌 수 있을까?
- 애그리게이트를 JPA를 기준으로 생각한다면 같은 라이프 사이클을 가진다면 동일한 범주의 애그리게이트로 나눌 수 있지 않을까?
728x90
반응형
'스터디 > 도메인 주도 개발 시작하기' 카테고리의 다른 글
바운디드 컨텍스트 (0) | 2023.02.18 |
---|---|
응용 서비스와 표현 영역 (0) | 2023.01.26 |
리포지터리와 모델 구현 (0) | 2023.01.06 |
애그리게이트 (0) | 2022.12.30 |
도메인 모델 시작하기 (0) | 2022.12.14 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 트랜잭셔널 아웃박스 패턴 스프링부트
- spring boot excel download paging
- spring boot excel download oom
- space based architecture
- spring boot redisson sorted set
- transactional outbox pattern spring boot
- transactional outbox pattern
- java userThread와 DaemonThread
- JDK Dynamic Proxy와 CGLIB의 차이
- microkernel architecture
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- 레이어드 아키텍처란
- spring boot redisson destributed lock
- spring boot redis 대기열 구현
- spring boot 엑셀 다운로드
- spring boot redisson 분산락 구현
- polling publisher spring boot
- pipeline architecture
- @ControllerAdvice
- 공간 기반 아키텍처
- 서비스 기반 아키텍처
- java ThreadLocal
- spring boot poi excel download
- 자바 백엔드 개발자 추천 도서
- pipe and filter architecture
- redis 대기열 구현
- service based architecture
- redis sorted set
- 람다 표현식
- redis sorted set으로 대기열 구현
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함