-
Notifications
You must be signed in to change notification settings - Fork 36
Round8 #203
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
Round8 #203
Changes from 15 commits
50cb2ae
4e82fc2
7bbc64b
cf0f11a
8951175
f47104f
35e230c
cb43b85
819d86a
092eb59
3174797
e40c4cd
b5920db
b191947
5193457
c67f50a
d049e64
b67b119
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.loopers.application.event.product; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| public record ProductViewedEvent( | ||
| Long memberId, // nullable (๋น๋ก๊ทธ์ธ ์ฌ์ฉ์๋ ์ถ์ ) | ||
| Long productId, | ||
| Long brandId, | ||
| LocalDateTime viewedAt | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package com.loopers.infrastructure.kafka; | ||
|
|
||
| import org.springframework.stereotype.Component; | ||
|
|
||
| /** | ||
| * Kafka Topic Router | ||
| * - ์ด๋ฒคํธ ํ์ ์ ๋ฐ๋ผ ์ ์ ํ Kafka ํ ํฝ์ ๋ฐํ | ||
| * - ํ ํฝ ๋ค์ด๋ฐ ๊ท์น: loopers.commerce.{event-name}-v1 | ||
| */ | ||
| @Component | ||
| public class KafkaTopicRouter { | ||
|
|
||
| private static final String TOPIC_PREFIX = "loopers.commerce."; | ||
| private static final String TOPIC_VERSION = "-v1"; | ||
|
|
||
| /** | ||
| * ์ด๋ฒคํธ ํ์ ์ ๋ง๋ ํ ํฝ ์ด๋ฆ ๋ฐํ | ||
| * | ||
| * @param eventType ์ด๋ฒคํธ ํ์ (ORDER_PLACED, PRODUCT_LIKED ๋ฑ) | ||
| * @return Kafka ํ ํฝ ์ด๋ฆ | ||
| */ | ||
| public String getTopicName(String eventType) { | ||
| return switch (eventType) { | ||
| // Order Events | ||
| case "ORDER_PLACED" -> TOPIC_PREFIX + "order-placed" + TOPIC_VERSION; | ||
| case "ORDER_COMPLETED" -> TOPIC_PREFIX + "order-completed" + TOPIC_VERSION; | ||
|
|
||
| // Payment Events | ||
| case "PAYMENT_COMPLETED" -> TOPIC_PREFIX + "payment-completed" + TOPIC_VERSION; | ||
|
|
||
| // Product Events | ||
| case "PRODUCT_LIKED" -> TOPIC_PREFIX + "product-liked" + TOPIC_VERSION; | ||
| case "PRODUCT_UNLIKED" -> TOPIC_PREFIX + "product-unliked" + TOPIC_VERSION; | ||
| case "PRODUCT_VIEWED" -> TOPIC_PREFIX + "product-viewed" + TOPIC_VERSION; | ||
|
|
||
| default -> throw new IllegalArgumentException("Unknown event type: " + eventType); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * ํ ํฝ ์ด๋ฆ์ด ์ ํจํ์ง ๊ฒ์ฆ | ||
| */ | ||
| public boolean isValidTopic(String topicName) { | ||
| return topicName != null && | ||
| topicName.startsWith(TOPIC_PREFIX) && | ||
| topicName.endsWith(TOPIC_VERSION); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐งฉ Analysis chain๐ Script executed: #!/bin/bash
# Description: isValidTopic ๋ฉ์๋ ์ฌ์ฉ์ฒ ํ์ธ
rg -n "isValidTopic" --type=javaRepository: Loopers-dev-lab/loopers-spring-java-template Length of output: 229
ํ์ฌ ์ฝ๋๋ฒ ์ด์ค์์ ์ด ๋ฉ์๋์ ํธ์ถ ์ง์ ์ ์ฐพ์ ์ ์์ต๋๋ค. ์ฌ์ฉ๋์ง ์๋ ๋ฉ์๋์ด๋ฏ๋ก ์ ๊ฑฐํ๊ฑฐ๋, ํฅํ ์ฌ์ฉ ๊ณํ์ด ์๋ค๋ฉด TODO ์ฃผ์์ ์ถ๊ฐํ์ธ์. ๐ค Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package com.loopers.infrastructure.kafka.event; | ||
|
|
||
| import java.time.ZonedDateTime; | ||
|
|
||
| /** | ||
| * Kafka ์ด๋ฒคํธ Envelope | ||
| * - Application Event๋ฅผ Kafka๋ก ์ ์กํ ๋ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๊ธฐ ์ํ ๋ํผ | ||
| * - ๋ฉฑ๋ฑ์ฑ ์ฒดํฌ ๋ฐ ์์ ๋ณด์ฅ์ ์ํ ์ ๋ณด ํฌํจ | ||
| * | ||
| * @param <T> Application Event ํ์ (OrderPlacedEvent, ProductLikedEvent ๋ฑ) | ||
| */ | ||
| public record KafkaEventEnvelope<T>( | ||
| /** | ||
| * ์ด๋ฒคํธ ID (Outbox Event์ ID) | ||
| * - Consumer์์ ๋ฉฑ๋ฑ์ฑ ์ฒดํฌ์ ์ฌ์ฉ | ||
| */ | ||
| String eventId, | ||
|
|
||
| /** | ||
| * ์ด๋ฒคํธ ํ์ | ||
| * - ์: ORDER_PLACED, PRODUCT_LIKED, PAYMENT_COMPLETED | ||
| */ | ||
| String eventType, | ||
|
|
||
| /** | ||
| * Partition Key | ||
| * - Kafka ํํฐ์ ๋ถ๋ฐฐ ๊ธฐ์ค | ||
| * - ๊ฐ์ ๊ฐ์ ํญ์ ๊ฐ์ ํํฐ์ ์ผ๋ก ์ ์ก๋์ด ์์ ๋ณด์ฅ | ||
| */ | ||
| String partitionKey, | ||
|
|
||
| /** | ||
| * ์ค์ ์ด๋ฒคํธ Payload | ||
| * - Application Event ๊ฐ์ฒด (OrderPlacedEvent, ProductLikedEvent ๋ฑ) | ||
| */ | ||
| T payload, | ||
|
|
||
| /** | ||
| * ์ด๋ฒคํธ ๋ฐ์ ์๊ฐ | ||
| */ | ||
| ZonedDateTime occurredAt | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| package com.loopers.infrastructure.outbox; | ||
|
|
||
| import com.loopers.application.event.like.ProductLikedEvent; | ||
| import com.loopers.application.event.like.ProductUnlikedEvent; | ||
| import com.loopers.application.event.order.OrderCompletedEvent; | ||
| import com.loopers.application.event.order.OrderPlacedEvent; | ||
| import com.loopers.application.event.payment.PaymentCompletedEvent; | ||
| import com.loopers.application.event.product.ProductViewedEvent; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.transaction.annotation.Propagation; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
| import org.springframework.transaction.event.TransactionPhase; | ||
| import org.springframework.transaction.event.TransactionalEventListener; | ||
|
|
||
| /** | ||
| * Kafka Outbox Event Listener | ||
| * - Application Event๋ฅผ ๋ฐ์์ Outbox ํ ์ด๋ธ์ ์ ์ฅ | ||
| * - BEFORE_COMMIT: ๊ฐ์ ํธ๋์ญ์ ๋ด์์ ์ฒ๋ฆฌ๋์ด ์์์ฑ ๋ณด์ฅ | ||
| */ | ||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class KafkaOutboxEventListener { | ||
|
|
||
| private final OutboxEventWriter outboxWriter; | ||
|
|
||
| /** | ||
| * ์ฃผ๋ฌธ ์์ฑ ์ด๋ฒคํธ โ Outbox ์ ์ฅ | ||
| */ | ||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| public void handleOrderPlaced(OrderPlacedEvent event) { | ||
| log.debug("[Outbox] OrderPlacedEvent ์์ - orderNo: {}", event.orderNo()); | ||
|
|
||
| outboxWriter.write( | ||
| event.orderNo(), // partition key | ||
| "ORDER_PLACED", // event type | ||
| event // payload | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * ์ฃผ๋ฌธ ์๋ฃ ์ด๋ฒคํธ โ Outbox ์ ์ฅ | ||
| */ | ||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| public void handleOrderCompleted(OrderCompletedEvent event) { | ||
| log.debug("[Outbox] OrderCompletedEvent ์์ - orderNo: {}", event.orderNo()); | ||
|
|
||
| outboxWriter.write( | ||
| event.orderNo(), | ||
| "ORDER_COMPLETED", | ||
| event | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * ๊ฒฐ์ ์๋ฃ ์ด๋ฒคํธ โ Outbox ์ ์ฅ | ||
| */ | ||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| public void handlePaymentCompleted(PaymentCompletedEvent event) { | ||
| log.debug("[Outbox] PaymentCompletedEvent ์์ - orderNo: {}", event.orderNo()); | ||
|
|
||
| outboxWriter.write( | ||
| event.orderNo(), | ||
| "PAYMENT_COMPLETED", | ||
| event | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * ์ํ ์ข์์ ์ด๋ฒคํธ โ Outbox ์ ์ฅ | ||
| */ | ||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| public void handleProductLiked(ProductLikedEvent event) { | ||
| log.debug("[Outbox] ProductLikedEvent ์์ - productId: {}", event.productId()); | ||
|
|
||
| outboxWriter.write( | ||
| String.valueOf(event.productId()), // partition key | ||
| "PRODUCT_LIKED", | ||
| event | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * ์ํ ์ข์์ ์ทจ์ ์ด๋ฒคํธ โ Outbox ์ ์ฅ | ||
| */ | ||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| public void handleProductUnliked(ProductUnlikedEvent event) { | ||
| log.debug("[Outbox] ProductUnlikedEvent ์์ - productId: {}", event.productId()); | ||
|
|
||
| outboxWriter.write( | ||
| String.valueOf(event.productId()), | ||
| "PRODUCT_UNLIKED", | ||
| event | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * ์ํ ์กฐํ ์ด๋ฒคํธ โ Outbox ์ ์ฅ | ||
| */ | ||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| @Transactional(propagation = Propagation.REQUIRES_NEW) | ||
| public void handleProductViewed(ProductViewedEvent event) { | ||
| log.debug("[Outbox] ProductViewedEvent ์์ - productId: {}", event.productId()); | ||
|
|
||
| outboxWriter.write( | ||
| String.valueOf(event.productId()), // partition key | ||
| "PRODUCT_VIEWED", | ||
| event | ||
| ); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.