Skip to content

Commit 791b570

Browse files
committed
[#39] 쿠폰 중복 발급 검증을 API 레이어로 이동 및 캐시-DB 2단계 조회 개선
1. 쿠폰 발급 요청 시 컨트롤러에서 사전 중복 검증 수행 2. isIssued → checkCouponIssueStatus 리네이밍 3. 캐시 miss 시 DB fallback 조회 후 캐시 갱신(cache warming) 추가 4. issueCoupon에서 중복 검증 로직 제거 (컨트롤러로 이동)
1 parent bc2d83e commit 791b570

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

coupon/src/main/java/com/commerce/coupon/bootstrap/CouponController.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,16 @@ public ResponseEntity<String> issueCoupon(
2525
@PathVariable String couponId,
2626
@PathVariable String customerId
2727
) {
28-
couponIssueUseCase.requestIssueCoupon(CouponId.of(couponId), CustomerId.of(customerId));
28+
CouponId couId = CouponId.of(couponId);
29+
CustomerId cusId = CustomerId.of(customerId);
30+
31+
boolean issued = couponIssueUseCase.checkCouponIssueStatus(couId, cusId);
32+
if(issued) {
33+
return ResponseEntity.ok("이미 발급된 쿠폰입니다.");
34+
}
35+
36+
// 미발행된 경우에만 이벤트 발행
37+
couponIssueUseCase.requestIssueCoupon(couId, cusId);
2938
return ResponseEntity.ok("발급 요청 완료");
3039
}
3140

@@ -44,7 +53,7 @@ public ResponseEntity<String> checkIssued(
4453
@PathVariable String couponId,
4554
@PathVariable String customerId
4655
) {
47-
boolean isIssued = couponIssueUseCase.isIssued(
56+
boolean isIssued = couponIssueUseCase.checkCouponIssueStatus(
4857
CouponId.of(couponId),
4958
CustomerId.of(customerId)
5059
);

coupon/src/main/java/com/commerce/coupon/core/application/port/in/CouponIssueUseCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ public interface CouponIssueUseCase {
1010
List<CouponView> getMyCoupons(CustomerId customerId);
1111
void issueCoupon(CouponId couponId, CustomerId customerId);
1212
void requestIssueCoupon(CouponId couponId, CustomerId customerId);
13-
boolean isIssued(CouponId of, CustomerId of1);
13+
boolean checkCouponIssueStatus(CouponId couponId, CustomerId customerId);
1414
}

coupon/src/main/java/com/commerce/coupon/core/application/port/in/CouponIssueUseCaseImpl.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
import java.time.LocalDate;
2222
import java.util.List;
2323
import java.util.Map;
24+
import java.util.concurrent.atomic.AtomicBoolean;
2425
import java.util.function.Function;
2526
import java.util.stream.Collectors;
2627

27-
import static com.commerce.shared.exception.BusinessError.DUPLICATE_ISSUED_COUPON;
2828
import static com.commerce.shared.exception.BusinessError.INVALID_COUPON;
2929
import static com.commerce.shared.kafka.event.topic.EventTopic.COUPON_ISSUE_TOPIC;
3030

@@ -68,16 +68,10 @@ public List<CouponView> getMyCoupons(CustomerId customerId) {
6868

6969
@Override
7070
public void issueCoupon(CouponId couponId, CustomerId customerId) {
71-
// 캐싱데이터 조회
72-
if(isIssued(couponId, customerId)) return;
73-
74-
// db에서 발행여부 조회
75-
CouponIssueId couponIssueId = new CouponIssueId(couponId, customerId);
76-
couponIssueOutPort.findByCouponIssueId(couponIssueId)
77-
.ifPresent(exist -> {
78-
throw new BusinessException(DUPLICATE_ISSUED_COUPON);
79-
});
80-
71+
// todo 큐이기 때문에 중복 요청이 올 수 있다. redis 검증로직이 필요하다.
72+
// todo 발행실패인 경우, 멱등성있게 처리하는 방법이 필요하다.
73+
// todo 테스트코드 수정
74+
8175
// 발행
8276
Coupon coupon = couponOutPort.findById(couponId)
8377
.orElseThrow(() -> new BusinessException(INVALID_COUPON));
@@ -97,7 +91,22 @@ public void requestIssueCoupon(CouponId couponId, CustomerId customerId) {
9791
}
9892

9993
@Override
100-
public boolean isIssued(CouponId couponId, CustomerId customerId) {
101-
return couponIssueCache.isIssued(couponId, customerId);
94+
public boolean checkCouponIssueStatus(CouponId couponId, CustomerId customerId) {
95+
// 쿠폰발행 캐싱 확인
96+
AtomicBoolean issued = new AtomicBoolean(couponIssueCache.isIssued(couponId, customerId));
97+
98+
if(issued.get()){
99+
return true;
100+
}
101+
102+
// cache miss or 미발행
103+
CouponIssueId couponIssueId = new CouponIssueId(couponId, customerId);
104+
couponIssueOutPort.findByCouponIssueId(couponIssueId)
105+
.ifPresent(exist -> {
106+
couponIssueCache.save(couponId, customerId);
107+
issued.set(true);
108+
});
109+
110+
return issued.get();
102111
}
103112
}

0 commit comments

Comments
 (0)