[4주차] 쿠폰 신규 도메인 개발, 동시성 제어 및트랜잭션 관리 - 안성훈#171
[4주차] 쿠폰 신규 도메인 개발, 동시성 제어 및트랜잭션 관리 - 안성훈#171shAn-kor wants to merge 17 commits intoLoopers-dev-lab:shAn-korfrom
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📌 Summary
배경: 커머스 도메인에서 재고 변경, 쿠폰 사용, 좋아요 변경 등 동시 요청 시 정합성 문제가 발생할 수 있습니다.
목표: 각 도메인의 동시성 리스크를 분석하고, 리스크 수준과 경합 특성에 맞는 동시성 제어 전략을 적용하여 데이터 정합성을 보장합니다.
결과: 3개 도메인에 맞는 동시성 전략을 적용하고, CountDownLatch + ExecutorService 기반 동시성 통합 테스트로 검증을 완료했습니다. 쿠폰 도메인 신규 구현 및 주문-쿠폰 연동도 포함됩니다.
🧭 Context & Decision
문제 정의
OrderFacade가 재고/쿠폰/취소 조합 로직을 흩어서 보유하고 있었고, 쿠폰 사용은 엔티티 상태 직접 변경 방식이라 동시성 경합 시 정합성 보장이 불명확했다.AVAILABLE → USED상태 전이가 비원자적이라 중복 사용 가능성이 있었다.OrderUseCase.cancel)에서 트랜잭션으로 처리된다.선택지와 결정
OrderFacade를 유지하면서 쿠폰 원자 업데이트만 추가 — Facade 로직 분산 문제가 해결되지 않음OrderUseCase로 통합,OrderFacade제거, 쿠폰·재고·좋아요 모두 원자 업데이트로 확정OrderUseCase로 단일 오케스트레이션, Facade는BrandAdminFacade·LikeFacade·ProductQueryFacade등 순수 조합 목적만 잔존🏗️ Design Overview
변경 범위
apps/commerce-api(전체 도메인),modules/jpa(BaseEntity UUID),modules/redis(Testcontainers)OrderUseCase— 주문 생성·취소 오케스트레이션 (재고 예약 + 쿠폰 사용 + 취소 복구)CouponApplicationService— 쿠폰 사용/사용취소 원자 업데이트CouponAdminApplicationService— 쿠폰 생성·발급 어드민 유즈케이스ProductStockApplicationService— 재고 3전략(원자/낙관/비관) 메서드 분리docs/adr/2026-03-05-stock-deduction-concurrency-adr.md— 동시성 전략 ADRdocs/sql/schema-uuid-binary16.sql— UUID 스키마 DDLOrderFacade→OrderUseCase로 대체 (크로스도메인 조합 책임 이전)주요 컴포넌트 책임
OrderUseCase: 주문 생성(재고 예약 → 쿠폰 사용 → 주문 저장) 및 취소(주문 상태 → 재고 복구 → 쿠폰 복구) 트랜잭션 오케스트레이션CouponApplicationService:markUsedAtomically/markAvailableAtomically— 조건부 JPQL update, 0 row = CONFLICT(409)ProductStockApplicationService:decreaseStockAtomically/decreaseStockWithOptimisticLock/decreaseStockWithPessimisticLock전략 노출IssuedCouponJpaRepository:@Modifying조건부 update 쿼리 (AVAILABLE→USED, USED→AVAILABLE)BaseEntity(modules/jpa):@Id타입을UUID로 변경,@Column(columnDefinition = "BINARY(16)")적용🔁 Flow Diagram
Main Flow — 주문 생성 (쿠폰 적용)
sequenceDiagram autonumber participant Client participant OrderController participant OrderUseCase participant ProductStockAppSvc participant CouponAppSvc participant OrderAppSvc participant DB Client->>OrderController: POST /orders (productId, couponId?) OrderController->>OrderUseCase: create(CreateOrderCommand) OrderUseCase->>ProductStockAppSvc: reserveStock(productId, qty) ProductStockAppSvc->>DB: UPDATE stock SET stock = stock - qty WHERE stock >= qty DB-->>ProductStockAppSvc: affected rows (0 = 품절 → 409) alt couponId 있음 OrderUseCase->>CouponAppSvc: use(UseCouponCommand) CouponAppSvc->>DB: UPDATE issued_coupon SET status='USED' WHERE id=? AND status='AVAILABLE' DB-->>CouponAppSvc: affected rows (0 = 이미 사용 → 409) end OrderUseCase->>OrderAppSvc: save(order) OrderAppSvc->>DB: INSERT order DB-->>OrderUseCase: saved order OrderUseCase-->>OrderController: OrderView OrderController-->>Client: 201 CreatedCancel Flow — 주문 취소 (재고 + 쿠폰 복구)
sequenceDiagram autonumber participant Client participant OrderController participant OrderUseCase participant OrderAppSvc participant ProductStockAppSvc participant CouponAppSvc participant DB Client->>OrderController: DELETE /orders/{orderId} OrderController->>OrderUseCase: cancel(orderId, memberId) OrderUseCase->>OrderAppSvc: cancel(orderId, memberId) OrderAppSvc->>DB: UPDATE order SET status='CANCELLED' DB-->>OrderAppSvc: CancelledOrder (재고qty, couponId, 소유 memberId) OrderUseCase->>ProductStockAppSvc: restoreStock(productId, qty) ProductStockAppSvc->>DB: UPDATE stock SET stock = stock + qty alt 쿠폰 사용 이력 있음 OrderUseCase->>CouponAppSvc: cancelUse(couponId, memberId) CouponAppSvc->>DB: UPDATE issued_coupon SET status='AVAILABLE' WHERE status='USED' end OrderUseCase-->>OrderController: void OrderController-->>Client: 204 No Content✅ 테스트 커버리지
신규 테스트 목록
IssuedCouponDomainTestCouponDomainTestCouponApplicationServiceIntegrationTestCouponAdminApplicationServiceIntegrationTestOrderUseCaseIntegrationTestCouponApiE2ETestCouponUseConcurrencyTestProductStockApplicationServiceConcurrencyTest재고 동시성 지표 (20 req / 10 stock)
📎 관련 문서 / 커밋
커밋 목록 (volume4, 17 commits on top of 7fe2ff7)
관련 문서
docs/adr/2026-03-05-stock-deduction-concurrency-adr.md— 동시성 전략 ADRdocs/sql/schema-uuid-binary16.sql— UUID 스키마 DDLdocs/design/— 요구사항·시퀀스·클래스·ERD 설계문서 (업데이트 완료)docs/checklist.md— 구현 체크리스트