/////
Search
Duplicate

24년 1월 29일 월요일

날짜
2024/01/29

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
복사