-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 선착순 구매 기능 추가 #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/purchase-crud
Are you sure you want to change the base?
Conversation
| var productType: ProductType, | ||
|
|
||
| var reservationDeadline: LocalDateTime? = null, | ||
| var startDt: LocalDateTime? = null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p3. createdAt 과 같은 네이밍 규칙이 존재하는데, startedAt이 아닌 이유가 있을까요?
|
|
||
| ) { | ||
| fun update(request: UpdateProductRequest) { | ||
| if (this.status == ProductStatus.ACTIVE && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p3. 업데이트시에 어떤 정책들을 통해 검증하고 있는지 유닛 테스트가 필요해보이네요.
| override fun getStockVersionKey(productId: String): String = | ||
| "stock:v${LocalDate.now()}:$productId" | ||
|
|
||
| @Transactional(readOnly = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q. 여기에 트랜잭션 어노테이션이 있는 경우, 어떤 역할을 수행하게 되나요?
|
|
||
| fun initializeForVersion(products: List<ProductEntity>, version: String) { | ||
| val stockMap = products.associate { product -> | ||
| val key = "stock:$version:${product.id}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p1. 현재 레디스의 키들을 만드는 로직들이 전반적으로 서비스 레이어에 파편화 되어 있는 것 같습니다. 이 부분을 별도의 Pure 객체를 정의하고, 이를 토대로 의미론적으로 key들을 반환받도록 리팩토링할 필요가 있어보입니다. 예를 들어, InMemoryStock 과 같이 별도의 도메인 객체를 만들고, 전달 받은 파라미터들을 토대로 key를 생성하는 등 일관되게 관리할 수 있도록 정책 객체를 따로 두어야할 것 같아요.
|
|
||
| fun initializeProductCache(products: List<ProductEntity>, version: String) { | ||
| val cacheMap: Map<String, String> = products.associate { product -> | ||
| val key = "product:${version}:${product.id}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p3. product과 stock을 stockService에서 제어하고 있는데, 이 부분이 역할적인 측면에서 맞을까요?
| end | ||
| """.trimIndent() | ||
|
|
||
| val result = redisTemplate.execute( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p3. 실행시 발생할 수 있는 예외들을 재정의할 필요가 있어보입니다. 예를 들어, 찾고자 하는 stock이 없거나, quantity의 값이 음수가 될 수 있는 등 다양한 케이스들에 대해 예외들을 잘 정의해야할 것 같아요.
…into feature/purchase-create-detail # Conflicts: # app/src/main/kotlin/com/nearpick/app/domain/purchase/controller/PurchaseController.kt # domain/src/main/kotlin/com/nearpick/app/domain/purchase/service/PurchaseService.kt
|



Description
이번 PR에서는 선착순 구매(First-Come Purchase) 기능의 핵심 흐름과,
이를 지원하기 위한 Quartz 기반 재고 초기화 Job을 구현했습니다.
상품 타입이 FIRST_COME인 경우, 구매 시 Redis를 통해 재고를 실시간으로 제어하도록 처리했습니다.
Quartz Job을 통해 매일 새벽 KST 02:00에 Redis에 재고와 상품 정보를 미리 세팅(TTL=2일) 합니다.
구매 요청은 Kafka 이벤트(first-come-created)로 발행되어 Consumer에서 DB에 영속화됩니다.
Quartz 재고 초기화 Sequence diagram
선착순 구매 Sequence diagram
Kafka Consumer 실패 정책은 아직 적용되지 않아, 메시지 처리 중 예외가 발생할 경우 DLQ나 재시도 없이 kafka 성공 기준으로만 동작하고 있습니다.
Quartz 기반 상품 상태 점검 Job은 미구현 상태로, 상품 상태(INACTIVE_SOLD_OUT, INACTIVE_PERIOD) 변경을 주기적으로 수행하는 스케줄이 추가될 예정입니다. (sold out은 재고 부족 시 자동으로 처리할 예정이지만 제대로 처리가 되지 않았을 경우를 대비한 스케쥴을 추가할 예정입니다.)
테스트 코드와 장애 복구 시나리오(Kafka 전송 실패, Redis 연결 단절과 같은 상황에 대한 fallback 로직)이 추가될 예정입니다.
파일 관련 추가 설명
PurchaseService.kt: 상품 타입 기반으로 구매 전략 선택
PurchaseStrategyFactory.kt: FIRST_COME 전략 분기 및 주입
FirstComePurchaseStrategy.kt: Redis 재고 확인, 감소, Kafka 이벤트 발행
StockService.kt: Redis에 접근하는 모든 상품과 재고 관련 로직 (상품 캐시(getProductCache..), 재고(getStock, decrementStock, recoverStock..)..)
KafkaProducer.kt: PurchaseCreatedEvent 발행 (topic="first-come-created")
PurchaseCreatedEventConsumer.kt: groupId="nearpick-group"으로 이벤트 수신 후 DB 저장
Related Issues :
#24
Test cases
Redis, kafka를 이용한 선착순 구매 로직의 정상 동작을 확인했습니다.
Kafka 이벤트 발행(purchase.created) 후 비동기 구매 처리 정상 수행 여부 확인했습니다.
Need additional checks?
Types of changes
Additional Context
이후 이 부분 관련한 테스트 코드 작성과 타임아웃, 중복 방지, 예외 발생 시 재시도 등 예외 처리를 추가할 예정입니다.