Skip to content

Commit fee55a1

Browse files
committed
feat: Kafka 기반 주문 이벤트 발행
Application Event에서 Kafka 기반 이벤트 방식으로 변경
1 parent dadc0cc commit fee55a1

2 files changed

Lines changed: 46 additions & 40 deletions

File tree

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
package com.loopers.application.order;
22

33
import com.loopers.application.orderitem.OrderItemInfo;
4-
import com.loopers.application.product.UserActionEvent;
54
import com.loopers.domain.coupon.Coupon;
65
import com.loopers.domain.coupon.CouponRepository;
76
import com.loopers.domain.order.Order;
87
import com.loopers.domain.order.OrderRepository;
98
import com.loopers.domain.orderitem.OrderItem;
109
import com.loopers.domain.orderitem.OrderItemRepository;
10+
import com.loopers.domain.outbox.AggregateType;
11+
import com.loopers.domain.outbox.OutboxEvent;
12+
import com.loopers.domain.outbox.OutboxRepository;
13+
import com.loopers.domain.outbox.OutboxType;
1114
import com.loopers.domain.product.Product;
1215
import com.loopers.domain.product.ProductRepository;
1316
import com.loopers.domain.user.UserRepository;
1417
import com.loopers.interfaces.api.order.OrderV1Dto;
15-
import com.loopers.interfaces.api.payment.PaymentV1Dto;
1618
import com.loopers.support.error.CoreException;
1719
import com.loopers.support.error.ErrorType;
1820
import lombok.RequiredArgsConstructor;
19-
import org.springframework.context.ApplicationEventPublisher;
2021
import org.springframework.stereotype.Component;
2122
import org.springframework.transaction.annotation.Transactional;
2223

@@ -31,7 +32,7 @@ public class OrderFacade {
3132
private final UserRepository userRepository;
3233
private final ProductRepository productRepository;
3334
private final CouponRepository couponRepository;
34-
private final ApplicationEventPublisher publisher;
35+
private final OutboxRepository outBoxRepository;
3536

3637
@Transactional
3738
public OrderResultInfo createOrder(OrderV1Dto.OrderRequest request) {
@@ -50,31 +51,31 @@ public OrderResultInfo createOrder(OrderV1Dto.OrderRequest request) {
5051
List<OrderV1Dto.OrderItemRequest> orderItemRequests = request.orderItems();
5152

5253
List<OrderItem> orderItems = orderItemRequests.stream()
53-
.map(item -> {
54-
// 상품 검증
55-
Long productId = item.productId();
56-
Product product = productRepository.findById(productId).orElseThrow(
57-
() -> new CoreException(ErrorType.NOT_FOUND, "존재하는 상품이 아닙니다.")
58-
);
59-
60-
if (product.getStock() < item.quantity()) {
61-
throw new CoreException(ErrorType.BAD_REQUEST, product.getName() + " 상품의 재고가 부족합니다.");
62-
}
63-
64-
product.decreaseStock(item.quantity());
65-
66-
OrderItem orderItem = item.toEntity(
67-
null,
68-
product.getPrice().multiply(BigDecimal.valueOf(item.quantity()))
69-
);
70-
return orderItem;
71-
72-
})
73-
.toList();
54+
.map(item -> {
55+
// 상품 검증
56+
Long productId = item.productId();
57+
Product product = productRepository.findById(productId).orElseThrow(
58+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하는 상품이 아닙니다.")
59+
);
60+
61+
if (product.getStock() < item.quantity()) {
62+
throw new CoreException(ErrorType.BAD_REQUEST, product.getName() + " 상품의 재고가 부족합니다.");
63+
}
64+
65+
product.decreaseStock(item.quantity());
66+
67+
OrderItem orderItem = item.toEntity(
68+
null,
69+
product.getPrice().multiply(BigDecimal.valueOf(item.quantity()))
70+
);
71+
return orderItem;
72+
73+
})
74+
.toList();
7475

7576
BigDecimal totalPrice = orderItems.stream()
76-
.map(OrderItem::getOrderPrice)
77-
.reduce(BigDecimal.ZERO, BigDecimal::add);
77+
.map(OrderItem::getOrderPrice)
78+
.reduce(BigDecimal.ZERO, BigDecimal::add);
7879

7980
Long couponId = request.couponId();
8081

@@ -92,27 +93,28 @@ public OrderResultInfo createOrder(OrderV1Dto.OrderRequest request) {
9293
.multiply(BigDecimal.valueOf(100 - rate))
9394
.divide(BigDecimal.valueOf(100));
9495

95-
publisher.publishEvent(new OrderCreatedEvent(couponId, null, null, null, null, null));
96+
coupon.useCoupon();
9697

9798
Order order = request.toEntity(totalPrice);
9899
Order saved = orderRepository.save(order);
99100

100101
orderItems.forEach(item -> item.assignOrderId(saved.getId()));
101-
orderItemRepository.saveAll(orderItems);
102+
List<OrderItem> savedOrderItems = orderItemRepository.saveAll(orderItems);
103+
104+
savedOrderItems.forEach(orderItem -> {
105+
OutboxEvent outboxEvent = OutboxEvent.of(
106+
AggregateType.PRODUCT,
107+
orderItem.getProductId(),
108+
OutboxType.PRODUCT_SALES
109+
);
102110

103-
List<OrderItemInfo> orderItemInfos = orderItems.stream()
111+
outBoxRepository.save(outboxEvent);
112+
});
113+
114+
List<OrderItemInfo> orderItemInfos = savedOrderItems.stream()
104115
.map(orderItem -> OrderItemInfo.from(orderItem, orderItem.getOrderPrice()))
105116
.toList();
106117

107-
publisher.publishEvent(new OrderCreatedEvent(
108-
couponId,
109-
"1351039135",
110-
PaymentV1Dto.CardTypeDto.HYUNDAI,
111-
"1234-5678-9814-1451",
112-
100000L,
113-
"http://localhost:8080/api/v1/examples/callback"
114-
));
115-
116118
return new OrderResultInfo(OrderInfo.from(saved), orderItemInfos);
117119
}
118120
}

apps/commerce-api/src/main/java/com/loopers/infrastructure/outbox/KafkaOutboxPublisher.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class KafkaOutboxPublisher {
2121
@Scheduled(fixedDelayString = "60000")
2222
@Transactional
2323
public void publishProductViewed() {
24-
List<OutboxEvent> events = outboxRepository.findPending(2);
24+
List<OutboxEvent> events = outboxRepository.findPending(10);
2525

2626
for (OutboxEvent event : events) {
2727
OutboxType type = event.getEventType();
@@ -34,6 +34,10 @@ else if (type.equals(OutboxType.PRODUCT_LIKED)) {
3434
kafkaTemplate.send("product-liked", String.valueOf(event.getAggregateId()), event);
3535
}
3636

37+
else if (type.equals(OutboxType.PRODUCT_SALES)) {
38+
kafkaTemplate.send("product-sales", String.valueOf(event.getAggregateId()), event);
39+
}
40+
3741
event.markAsProcessed();
3842
}
3943

0 commit comments

Comments
 (0)