CQRS 을 위한 AWS 설정 ??
문제
•
서로 다른 가용영역의 여러 개의 AWS RDS Single AZ 사용한 CQRS 구현 시 아래와 같은 이슈 발생
◦
Failover 를 지원하기에 상당히
◦
Synchronous Update 를 지원하려면 아래와 같은 서비스를 이용
▪
AWS DMS
▪
AWS Query Notification
▪
AWS SQS
•
Aurora를 사용하면 아래와 같은 이점이 있으나 단점도 존재함
◦
이점
▪
Secondary DB
•
최대 15개의 Secondary DB지원
•
하나의 엔드포인트만 애플리케이션에서 연결해도 여러 Secondary DB들로 로드밸런싱을 해준다.
◦
단점
▪
한 달에 800 불,,,
해결방안
•
10~20 달러를 조금 더 지불하여 Multiple AZ 에 대하여 Read Replica를 사용, 아래와 같은 이점을 챙김
◦
1개의 Primary에 대한 3개
◦
Synchronous Update
◦
Automatic fail over
◦
Automatic Standby Snapshots
개인적인 의견 (지훈)
•
AWS에 너무 치중하지 말고, AWS를 사용하지 않고도 어떻게 Failover를 지원하고 Synchronous Update 지원할 수 있을지도 생각해야함!!
각자 개선 및 확장/수정 하고 싶은 기능에 대해 이야기해보자
문제
•
각자 개선사항 및 확장/수정 기능에 대해서 이야기해보자.
•
To-Do를 재정렬하여 다시 구현사항에 집중하자
해결방안
•
소셜 회원가입/로그인
•
대시보드 >> 프로메테우스, Datadog [규빈님]
•
포인트 충전
•
랭킹 캐싱 [재윤님]
•
검색 기록 캐싱 [재윤님]
•
검수 >> 리팩토링
•
이미지 삭제 스케줄러 >> 람다 [지훈]
•
soft delete 리팩토링 [지훈]
대규모 공사
- 엔티티에도 상태값도 넣어야함
- 서비스 코드도 고쳐야하고
- 둥가둥가,,,
•
테스트 코드 & jacoco [규정님]
•
상태에 따른 판매 입찰내역, 완료내역 >> 확장 가능성 농후!!
입찰/완료 여부에 따른 내역 조회 API
/api/buy/history/onprogress, /api/buy/history/end, ,,,
-> /api/buy/history/{state}
전략패턴 사용해도 될지도??
•
주문에 대한 Aggregation 적용 *DDD
CQRS 패턴에서 Command 내에서 조회를 해야한다면 ??
문제
•
지금 기본적으로 service 레이어에서 cqs가 안 되어있는 이슈가 있음
◦
/product/service/command/ProductLikeService
◦
/product/service/query/ProductService
◦
/sell/service/SellService
•
CQRS 패턴을 위해 Command, Query 에 따른 다른 DB 사용 시, 아래와 같은 이슈가 발생
◦
Command 사용한 로직 내부에서 Query를 사용하게 되는 로직을 사용하게 되면 서로 다른 DB를 사용하게 됨
▪
Command ⇒ Primary DB
▪
Query ⇒ Secondary DB
해결방안
아래와 같은 접근으로 해결하기로 함
Service Layer 의 Command 와 Query를 나누기로 함 (은 무슨,,,)
•
실험을 해보니 가장 상위 계층의 트랜잭션에 따라 전파되는 모양
◦
만약 Outer : @Transactional , Inner : @Transactional(readOnly=true) 인 경우
▪
@Transactional 로 처리됨
▪
예상과 달리 내부에서 Secondary를 쓰지 않음
▪
Query 또한 Primary에 접근하여 처리
•
반면 Class 레벨로 @Transactional(readOnly=true)을 단 Query Service 호출 시, Secondary DB에 접근하는 것을 볼 수 있었다.
현재 Provder Layer에 대해서 아래와 같은 리팩토링이 필요함
•
각각의 역할에 따른 Method Extract
Ex
(이전)
@Service
@RequiredArgsConstructor
public class BuyNowProvider {
private final SellService sellService;
private final BuyQueryService buyQueryService;
private final OrderCommandService orderCommandService;
private final CouponQueryService couponQueryService;
private final CouponCommandService couponCommandService;
@Transactional
public BuyNowResponseDto buyNowProduct(User buyer, BuyNowRequestDto requestDto, Long productId) {
// 해당상품과 가격에 대한 판매입찰
Sell sell = sellService.getRecentSellBidof(productId, requestDto.price());
// 쿠폰 조회
Coupon coupon = getCoupon(requestDto.couponId(), buyer);
// 새로운 주문
Order order = saveOrder(sell, buyer, coupon);
// 판매입찰 기프티콘에 주문 저장
sell.getGifticon().updateOrder(order);
// 해당 판매입찰 삭제
sellService.delete(sell);
// 구매가능검증 :: 사용자 포인트가 finalPrice 보다 작다면 예외처리
// << 이건 컨트롤러 단에서 검증 해줘야하 하지 않을까???
buyQueryService.userPointCheck(buyer, order.getFinalPrice());
// 즉시구매자 포인트 감소
buyer.decreasePoint(order.getFinalPrice());
// 판매입찰자 포인트 증가
order.getSeller().increasePoint(order.getExpectedPrice());
// 매핑
return OrderMapper.INSTANCE.toBuyNowResponseDto(order);
}
private Coupon getCoupon(Long couponId, User buyer) {
if (couponId != null) {
Coupon coupon = couponQueryService.checkCoupon(couponId, buyer, CouponStatus.AVAILABLE);
couponCommandService.changeCouponStatus(coupon, CouponStatus.ALREADY_USED);
return coupon;
}
return null;
}
private Order saveOrder(Sell sell, User buyer, Coupon coupon) {
if (coupon != null) {
return orderCommandService.saveOrderOfSell(sell, buyer, coupon);
}
return orderCommandService.saveOrderOfSellNotCoupon(sell, buyer);
}
}
Java
복사
(이후)
@Service
@RequiredArgsConstructor
public class BuyNowProvider {
private final SellService sellService;
private final BuyQueryService buyQueryService;
private final OrderCommandService orderCommandService;
private final CouponQueryService couponQueryService;
private final CouponCommandService couponCommandService;
@Transactional
public BuyNowResponseDto buyNowProduct(User buyer, BuyNowRequestDto requestDto, Long productId) {
Sell sell = getRecentSellBid(productId, requestDto.getPrice());
Coupon coupon = getCoupon(requestDto.getCouponId(), buyer);
Order order = createOrder(sell, buyer, coupon);
finalizeTransaction(order, buyer);
return OrderMapper.INSTANCE.toBuyNowResponseDto(order);
}
private Sell getRecentSellBid(Long productId, BigDecimal price) {
return sellService.getRecentSellBidof(productId, price);
}
private Coupon getCoupon(Long couponId, User buyer) {
if (couponId != null) {
Coupon coupon = couponQueryService.checkCoupon(couponId, buyer, CouponStatus.AVAILABLE);
couponCommandService.changeCouponStatus(coupon, CouponStatus.ALREADY_USED);
return coupon;
}
return null;
}
private Order createOrder(Sell sell, User buyer, Coupon coupon) {
if (coupon != null) {
return orderCommandService.saveOrderOfSell(sell, buyer, coupon);
}
return orderCommandService.saveOrderOfSellNotCoupon(sell, buyer);
}
private void finalizeTransaction(Order order, User buyer) {
buyQueryService.userPointCheck(buyer, order.getFinalPrice());
buyer.decreasePoint(order.getFinalPrice());
order.getSeller().increasePoint(order.getExpectedPrice());
sellService.delete(order.getSell());
}
}
Java
복사