일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- AWS
- react
- 자바
- 혼공SQL
- jpa
- 인덱스
- 클린코드
- 기술면접
- 인프런백기선
- 자료구조
- 이펙티브 자바
- DDD
- mysql
- 이펙티브자바
- 알고리즘
- CleanCode
- aop
- 스프링부트와AWS로혼자구현하는웹서비스
- 자바예외
- SQL쿡북
- MariaDB
- java
- 이팩티브 자바
- AWS RDS
- 네트워크
- 도메인 주도 개발 시작하기
- 인프런김영한
- 자바스터디
- 알고리즘분석
- vue.js
- Today
- Total
기록이 힘이다.
[도메인 주도 개발 시작하기] 7. 도메인 서비스 본문
여러 애그리거트가 필요한 기능
도메인 영역의 코드를 작성하다 보면 한 애그리거트로 기능을 구현할 수 없을 때가 있다. 대표적인 예가 결제 금액 계산 로직이다. 실제 결제 금액을 계산할 때는 다음과 같은 내용이 필요하다.
- 상품 애그리거트 : 구매하는 상품의 가격이 필요하다. 또한 상품에 따라 배송비가 추가되기도 한다.
- 주문 애그리거트 : 상품별로 구매 개수가 필요하다.
- 할인 쿠폰 애그리거트 : 쿠폰별로 지정한 할인 금액이나 비율에 따라 주문 총금액을 할인한다.
- 회원 애그리거트 : 회원 등급에 따라 추가 할인이 가능하다
이 상황에서 실제 금액을 계산해야 하는 주체는 어떤 애그리거트일까?
이 할인 금액을 구하는 것은 누구 책임일까?
도메인 서비스
- 계산 로직: 여러 애그리거트가 필요한 계산 로직이나, 한 애그리거트에 넣기에는 다소 복잡한 계산 로직
- 외부 시스템 연동이 필요한 도메인 로직: 구현하기 위해 타 시스템을 사용해야 하는 도메인 로직
계산 로직과 도메인 서비스
도메인 영역의 애그리거트나 밸류와 같은 구성요소와 도메인 서비스를 비교할 때 다른 점은 도메인 서비스는 상태 없이 로직만 구현한다는 점이다. 도메인 서비스를 구현하는 데 필요한 상태는 다른 방법으로 전달받는다.
public class DiscountCalculationService {
public Money calculateDiscountAmounts(
List<OrderLIne> orderLines,
List<Coupon> coupons,
MemberGrade grade) {
Money couponDiscount = coupons.stream()
.map(coupon -> calculateDiscount(coupon))
.reduce(Money(0), (v1, v2) -> v1.add(v2));
Money membershipDiscount = calculateDiscount(orderer.getMember().getGrade());
return couponDiscount.add(membershipDiscount);
}
...
}
할인 계산 서비스를 사용하는 주체는 애그리거트가 될 수도 있고 응용 서비스가 될수도 있다. DiscountCalculationService를 다음과 같이 애그리거트의 결제 금액 계산 기능에 전달하면 사용 주체는 애그리거트가 된다.
public class Order {
public void calculateAmounts(
DiscountCalculationService disCalSvc, MemberGrade grade) {
Money totalAmounts = getTotalAmounts();
Money discountAmounts = disCalSvc.calculateDiscountAmounts(this.orderLInes, this.coupons, greade);
this.paymentAmounts = totalAmounts.minus(discountAmounts);
}
...
애그리거트 객체에 도메인 서비스를 전달하는 것은 응용 서비스 책임이다.
public class OrderService {
private DiscountCalculationService discountCalculationService;
@Transactional
public OrderNo placeOrder(OrderRequest orderRequest) {
OrderNo orderno = orderRepository.nextId();
Order order = createOrder(orderNo, orderRequest);
orderRepository.save(order);
// 응용 서비스 실행 후 표현 영역에서 필요한 값 리턴
return orderNo;
}
private Order createOrder(OrderNo orderNo, OrderRequest orderReq) {
Member member =findMember(orderReq.getOrdererId());
Order order = new Order(orderNo, orderReq.gerOrderLines(),
orderReq.getCoupons(), createOrderer(member),
orderReq.getShippingInfo());
order.calculateAmounts(this.discountCalculationService, member.getGrade());
return order;
}
...
}
애그리거트 메서드를 실행할 때 도메인 서비스를 인자로 전달하지 않고 반대로 도메인 서비스의 기능을 실행할 때 애그리거트를 전달하기도 한다. 이런 식으로 동작하는 것 중 하나가 계좌 이체 기능이다.
public class TransgerService {
public void transfer(Account fromAcc, Account toAcc, Money amounts) {
fromAcc.withdraw(amounts);
toAcc.credit(amounts);
}
}
특정 기능이 응용 서비스인지 도메인 서비스인지 감을 잡기 어려울 때는 해당 로직이 애그리거트의 상태를 변경하거나 애그리거트의 상태 값을 계산하는지 검사해 보면 된다.
외부 시스템 연동과 도메인 서비스
예를 들어 설문 조사 시스템과 사용자 역할 관리 시스템이 분리되어 있다고 하자. 설문 조사 시스템은 설문 조사를 생성할 때 사용자가 생성 권한을 가진 역할인지 확인하기 위해 역할 관리 시스템과 연동해야 한다.
public interface SurveyPermissionChecker{
boolean hasUserCreationPermission(String useId);
}
public class CreateSurveyService{
private SurveyPermissionChecker permissionChecker;
public Long createSurvey(CreateSurveyRequest req){
validate(req);
//도메인 서비스를 이용해서 외부 시스템 연동을 표현
if(!permissionChecker.hasUserCreationPermission(req.getRequestorId())){
throw new NoPermissionException();
}
...
}
}
'IT서적 > 도메인 주도 개발 시작하기' 카테고리의 다른 글
[도메인 주도 개발 시작하기] 9. 도메인 모델과 바운디드 컨텍스트 (0) | 2023.07.31 |
---|---|
[도메인 주도 개발 시작하기] 8. 애그리거트 트랜잭션 관리 (0) | 2023.07.29 |
[도메인 주도 개발 시작하기] 6. 응용 서비스와 표현 영역 (0) | 2023.07.29 |
[도메인 주도 개발 시작하기] 5. 스프링 데이터 JPA를 이용한 조회 기능 (0) | 2023.07.28 |
[도메인 주도 개발 시작하기] 4. 리포지터리와 모델 구현 (0) | 2023.07.27 |