22. AGGREGATE = 개념적으로 하나인 객체 군
Order
order
=
orderRepository.findById(orderId);
order.changeShippingInfo(newShippingInfo);
order.calcel();
완전체를 로딩
AGG
ROOT가
로직/
일관성(트랜잭션)
처리
책임
22
23. 도메인 모델에서 AGG 찾기
Showing Reservation Customer
Movie DiscountStrategy Rule
SeatNo Grade
23
24. 요구 사항에 따라AGG 경계 정의
예매시영화의시간과
좌석을할당한다 회원은등급을갖는다
영화는 상영일정을갖는다?
영화별로가격할인규칙이다르다
(영화별로다른가격할인을갖는다?)
24
25. AGG 경계 : 규칙〮트랜잭션 범위
상영관리자가
영화일정을추가해도
영화정보는바뀌지않음
컨텐츠담당자가
영화출연자정보를변경해도
상영일정은바뀌지않음
동시성 처리를 할 때,
Moive를 변경하는 동안 관련 Showing을 잠금 필요가 없음
Showing
Movie
25
26. AGG 경계 : 규칙〮트랜잭션 범위
Movie DiscountStrategy Rule
• 영화 별로 할인 정책이 고정된다.
• 할인 정책 별로 적용 가능 규칙을 갖는다.
Movie가 AGG
루트:
• movie.updateDescription(desc);
• movie.changeDiscountStrategies(discountStrategies);
• movie.calculateFee(showing);
26
27. AGG 경계 : SRP
Movie DiscountStrategy Rule
• Movie는 영화 정보 제공하는 책임만 갖도록 구성
• 할인 계산은 별도 모듈로 분리
FeeCalculator
feeCalculator.calculate(movie,
showing)
27
28. 편리한 참조?
Showing Reservation Customer
Movie
reservation.getShowing().getTime()
reservation.getShowing().getMovie().getTitle()
reservation.getCustomer().getName()
28
29. AGG 간 참조의 잠재적 문제
편한탐색을오용
(불필요한) 고민
showing.getMovie().changeDescription(…);
showing.changeDiscountStrategy(disStrategy);
//
Showing.java
public
void
changeDiscountStrategy(List<DiscountStrategy>
strategies)
{
this.movie.changeDiscountStrategy(strategies);
}
Lazy
Loading
vs
Earger
Loading?
OSIV?
29
30. AGG 간 참조의 잠재적 문제
확장 방해
Oracle
MySQL
Custom
Rule
Engine
30
31. AGG 간 ID로 참조하기
public
class
Showing
{
…
private
MovieId movieId;
…
}
public
class
Reservation
{
…
private
ShowingId showingId;
private
CustomerId customerId;
…
}
Showing Reservation Customer
31
32. 연관 객체 조합은 응용 서비스에서 처리
public
class
ReserveService
{
public
ReservationId
reserve(ShowingId
showingId,
CustomerId
customerId)
{
Showing
showing
=
showingRepository.findById(showingId);
checkNotNull(shwoing);
Movie
movie
=
movieRepository.findById(showing.getMovie());
checkNotNull(movie);
Customer
customer
=
customerRepository.findById(customerId);
checkNotNull(customer);
Reservation
reservation
=
reservationFactory.create(
showing,
customer,
movie.calculateFee(showing)
);
return
reservation.getId();
}
…
} 32
59. 이벤트 관련 구성 요소
59
이벤트 생성 주체
이벤트 디스패처
(이벤트 퍼블리셔)
이벤트 핸들러
(이벤트 구독자)
이벤트
60. 이벤트 용도 1
• 트리거
• 다른기능을수행하기 위한트리거로 이벤트를 사용
• 예시
• 예매를하면SMS로통지한다.
• 예매함이벤트àSMS
통지트리거
• 예매를취소하면환불한다.
• 예매취소이벤트à환불트리거
60
예매
취소
이벤트
디스패처
예매취소됨
이벤트
핸들러예매
취소됨
이벤트
예매
취소됨
이벤트
환불
처리
61. 이벤트 용도 2
• 데이터동기화
• 다른시스템간데이터동기화목적으로 이벤트 사용
• 예시
• 상영일정추가이벤트핸들러
à조회전용저장소에 추가데이터반영
61
상영일정
추가
이벤트
디스패처
일정추가됨
이벤트
핸들러일정
추가됨
이벤트
일정
추가됨
이벤트
일정데이터
추가
커맨드 모델
저장소
쿼리 모델
저장소
62. 도메인과 이벤트
• 도메인의 상태변경을이벤트로 표현
• "~할때","~가발생하면", "만약~하면" 등의요구사항이 실
제로상태변경인지 확인
• 예시
• "영화정보를변경할때" àMovieInfoChangedEvent
• "예매를취소하면" àReservationCanceledEvent
62
63. (도메인) 이벤트 발생과 처리
63
public
class
Reservation
{
public
void
cancel()
{
…취소로직
Events.raise(new
ReservationCanceledEvent(getId(),
getPaymentNo(),
…));
}
}
public
class
CancelReservationService
{
@Transactional
public
void
cancel(ReservationId
resId)
{
Events.register(
(ReservationCanceledEvent
evt)
-‐>
refundSvc.refund(evt.getPaymentNo())
);
Reservation
resv
=
findById(resId);
resv.cancel();
}
}
64. 이벤트 적용 장점
• 서로다른도메인영역의 로직이섞이는것방지
• 불필요한 결합(coupling)
제거
64
예매!
취소
이벤트!
디스패처
예매!취소됨
이벤트!
핸들러예매!!
취소됨
이벤트
예매!!
취소됨
이벤트
환불!
처리
예매 취소에 더 이상 환불 로직 없음
예매 도메인에서 환불 도메인으로의 의존 제거
65. 이벤트 적용 장점
• 이벤트핸들러 추가로기능확장
65
예매
취소
이벤트
디스패처
예매취소됨
이벤트
핸들러예매
취소됨
이벤트
예매
취소됨
이벤트
환불
처리
예매취소됨
이벤트
핸들러2
이메일
통지
예매
취소됨
이벤트
66. 동기 이벤트 처리의 단점
• 트랜잭션 처리문제,성능(처리량) 문제
66
public
class
ReservationService
{
@Transactional
public
void
reserve(MovieId
movieId,
ShowingId
showingId,
CustomerId
custId)
{
Events.register(
(ReservationCreatedEvent
evt)
-‐>
emailNotifier.notify(…)
);
…//
예약도메인로직에서이벤트발생
}
}
이메일 발송 중 익셉션이 발생하면?
이메일 서버가 응답 시간이 길면?
67. 비동기 : 이벤트 처리 시간
• "A할 때,
B해라" 요구사항에서 A와B의간격
• 도메인전문가의 '바로'는 '즉시 실행'이 아님
• 수용가능한지연허용범위가있음
• 예시
• 예매취소시, (늦어도 30분 이내에) 환급처리함
• 결제완료후, (늦어도 다음날7시부터) 배송상태를조회
할수있어야함
• 티켓예매시, (최대10분 안에) 분단위티켓판매 통계에반
영함
• 분단위티켓판매통계가10분주기로생성되어도 대세지장없음
67
69. 응용/인프라
비동기 이벤트 처리 방식 1
• 이벤트디스패처에서 메시지 큐에전송
69
이벤트
디스패처
메시지큐도메인
메시지
리스너
스토리지
도입시 고려사항
• 글로벌 트랜잭션
• 메시지큐가 비정상일 때 이벤트 재전송 방안
이벤트
핸들러
70. 비동기 이벤트 처리 방식 2
• 로컬이벤트핸들러가 DB에 저장
• 포워더가 이벤트를 전달
70
응용/인프라
이벤트
디스패처
로컬
핸들러
도메인
메시지
리스너
스토리지
메시지큐포워더
단일 트랜잭션으로
처리이벤트
저장
이벤트를 주기적으로 읽어와 전달
어디까지 전달했는지 추적
71. DB 저장과 포워더 구현 예
• 쇼핑몰, ERP, 택배사연동
71
쇼핑
도메인
쇼핑+이벤트
DB
포워더
주문/결제시
관련 이벤트를
한 트랜잭션으로 저장
ERP
연동모듈
택배사
연동모듈
1분 주기로 새로
전달할 이벤트를
읽어와 외부 시스템에
전달
72. 비동기 이벤트 처리 방식 3
• 로컬 이벤트핸들러가 DB에 저장
• 이벤트수신측에서 이벤트를 직접가져감(pull)
• 이벤트제공API 이용(RESTful
API등)
72
응용/인프라
이벤트
디스패처
로컬
핸들러
도메인
스토리지
이벤트
저장
이벤트
(REST)
API
이벤트
Fetcher
이벤트
핸들러
단일 트랜잭션으로 처리
읽어올 이벤트 범위를
지정해서 가져와 핸들러에 전달
이벤트 데이터 제공함
도메인과 같은 프로세스에서
실행해도 무방