Skip to content

Commit e0eb51f

Browse files
authored
Merge pull request #175 from jeonga1022/feat/volume-7
[volume-7] Decoupling with Event
2 parents 2eb9ec9 + 4a2c592 commit e0eb51f

54 files changed

Lines changed: 2453 additions & 361 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/commerce-api/src/main/java/com/loopers/CommerceApiApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import org.springframework.boot.autoconfigure.SpringBootApplication;
66
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
77
import org.springframework.cloud.openfeign.EnableFeignClients;
8+
import org.springframework.scheduling.annotation.EnableAsync;
89
import java.util.TimeZone;
910

11+
@EnableAsync
1012
@EnableFeignClients
1113
@ConfigurationPropertiesScan
1214
@SpringBootApplication
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.loopers.application.coupon.event;
2+
3+
import com.loopers.domain.coupon.CouponDomainService;
4+
import com.loopers.domain.coupon.event.CouponUsedEvent;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.transaction.annotation.Propagation;
9+
import org.springframework.transaction.annotation.Transactional;
10+
import org.springframework.transaction.event.TransactionPhase;
11+
import org.springframework.transaction.event.TransactionalEventListener;
12+
13+
@Slf4j
14+
@Component
15+
@RequiredArgsConstructor
16+
public class CouponEventHandler {
17+
18+
private final CouponDomainService couponDomainService;
19+
20+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
21+
@Transactional(propagation = Propagation.REQUIRES_NEW)
22+
public void handleCouponUsed(CouponUsedEvent event) {
23+
log.info("쿠폰 사용 이벤트 수신 (AFTER_COMMIT): couponId={}, orderId={}",
24+
event.getCouponId(), event.getOrderId());
25+
try {
26+
couponDomainService.useCoupon(event.getCouponId());
27+
log.info("쿠폰 사용 처리 완료: couponId={}", event.getCouponId());
28+
} catch (Exception e) {
29+
log.error("쿠폰 사용 처리 실패: couponId={}, error={}",
30+
event.getCouponId(), e.getMessage(), e);
31+
}
32+
}
33+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.loopers.application.like.event;
2+
3+
import com.loopers.domain.like.event.ProductLikedEvent;
4+
import com.loopers.domain.product.ProductRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.transaction.annotation.Propagation;
9+
import org.springframework.transaction.annotation.Transactional;
10+
import org.springframework.transaction.event.TransactionPhase;
11+
import org.springframework.transaction.event.TransactionalEventListener;
12+
13+
@Slf4j
14+
@Component
15+
@RequiredArgsConstructor
16+
public class ProductLikeEventHandler {
17+
18+
private final ProductRepository productRepository;
19+
20+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
21+
@Transactional(propagation = Propagation.REQUIRES_NEW)
22+
public void handleProductLiked(ProductLikedEvent event) {
23+
try {
24+
if (event.isLiked()) {
25+
productRepository.incrementLikeCount(event.getProductId());
26+
} else {
27+
productRepository.decrementLikeCount(event.getProductId());
28+
}
29+
log.info("좋아요 집계 완료: productId={}, liked={}", event.getProductId(), event.isLiked());
30+
} catch (Exception e) {
31+
// 집계 실패해도 좋아요 기록은 이미 저장됨
32+
// 배치에서 일관성 보정 예정
33+
log.warn("좋아요 집계 실패: productId={}, liked={}", event.getProductId(), event.isLiked(), e);
34+
}
35+
}
36+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.support.error.CoreException;
4+
import com.loopers.support.error.ErrorType;
5+
6+
public record CardInfo(String cardType, String cardNo) {
7+
8+
public CardInfo {
9+
if (cardType == null || cardType.isBlank()) {
10+
throw new CoreException(ErrorType.BAD_REQUEST, "카드 타입은 필수입니다.");
11+
}
12+
if (cardNo == null || cardNo.isBlank()) {
13+
throw new CoreException(ErrorType.BAD_REQUEST, "카드 번호는 필수입니다.");
14+
}
15+
}
16+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.interfaces.api.order.OrderDto;
4+
5+
import java.util.List;
6+
7+
public record OrderCreateCommand(
8+
String userId,
9+
List<OrderDto.OrderItemRequest> items,
10+
CardInfo cardInfo,
11+
Long couponId
12+
) {
13+
public static OrderCreateCommand forPointPayment(String userId, List<OrderDto.OrderItemRequest> items) {
14+
return new OrderCreateCommand(userId, items, null, null);
15+
}
16+
17+
public static OrderCreateCommand forPointPaymentWithCoupon(String userId, List<OrderDto.OrderItemRequest> items, Long couponId) {
18+
return new OrderCreateCommand(userId, items, null, couponId);
19+
}
20+
21+
public static OrderCreateCommand forCardPayment(String userId, List<OrderDto.OrderItemRequest> items, CardInfo cardInfo) {
22+
return new OrderCreateCommand(userId, items, cardInfo, null);
23+
}
24+
25+
public static OrderCreateCommand forCardPaymentWithCoupon(String userId, List<OrderDto.OrderItemRequest> items, CardInfo cardInfo, Long couponId) {
26+
return new OrderCreateCommand(userId, items, cardInfo, couponId);
27+
}
28+
29+
public boolean isCardPayment() {
30+
return cardInfo != null;
31+
}
32+
33+
public boolean hasCoupon() {
34+
return couponId != null;
35+
}
36+
}

0 commit comments

Comments
 (0)