Skip to content

Commit 4dbd4b0

Browse files
authored
Merge pull request #197 from yeonjiyeon/feature/week8
Feature/week8
2 parents ed219b7 + 91e7b8c commit 4dbd4b0

47 files changed

Lines changed: 830 additions & 84 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/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ dependencies {
22
// add-ons
33
implementation(project(":modules:jpa"))
44
implementation(project(":modules:redis"))
5+
implementation(project(":modules:kafka"))
56
implementation(project(":supports:jackson"))
67
implementation(project(":supports:logging"))
78
implementation(project(":supports:monitoring"))
89

10+
911
// web
1012
implementation("org.springframework.boot:spring-boot-starter-web")
1113
implementation("org.springframework.boot:spring-boot-starter-actuator")

apps/commerce-api/src/main/java/com/loopers/application/coupon/CouponUsageEventListener.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import lombok.RequiredArgsConstructor;
77
import org.springframework.orm.ObjectOptimisticLockingFailureException;
88
import org.springframework.stereotype.Component;
9+
import org.springframework.transaction.annotation.Propagation;
910
import org.springframework.transaction.annotation.Transactional;
1011
import org.springframework.transaction.event.TransactionPhase;
1112
import org.springframework.transaction.event.TransactionalEventListener;
@@ -17,7 +18,7 @@ public class CouponUsageEventListener {
1718
private final FailedEventStore failedEventStore;
1819

1920
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
20-
@Transactional
21+
@Transactional(propagation = Propagation.REQUIRES_NEW)
2122
public void handlePaymentCompletedEvent(PaymentCompletedEvent event) {
2223

2324
if (event.couponId() == null) return;

apps/commerce-api/src/main/java/com/loopers/application/event/DeadLetterQueueProcessor.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44
import com.loopers.domain.event.DomainEvent;
55
import com.loopers.domain.event.FailedEvent;
66
import com.loopers.infrastructure.event.FailedEventRepository;
7+
import java.util.List;
78
import lombok.RequiredArgsConstructor;
8-
import lombok.extern.slf4j.Slf4j;
99
import org.springframework.context.ApplicationEventPublisher;
1010
import org.springframework.scheduling.annotation.Scheduled;
1111
import org.springframework.stereotype.Component;
1212
import org.springframework.transaction.annotation.Transactional;
1313

14-
import java.util.List;
15-
1614
@Component
1715
@RequiredArgsConstructor
1816
public class DeadLetterQueueProcessor {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.loopers.application.event;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.loopers.domain.event.OutboxEvent;
5+
import com.loopers.domain.event.OutboxStatus;
6+
import com.loopers.infrastructure.event.OutboxRepository;
7+
import java.util.List;
8+
import lombok.RequiredArgsConstructor;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.springframework.context.ApplicationEventPublisher;
11+
import org.springframework.scheduling.annotation.Scheduled;
12+
import org.springframework.stereotype.Component;
13+
import org.springframework.transaction.annotation.Transactional;
14+
15+
@Component
16+
@RequiredArgsConstructor
17+
@Slf4j
18+
public class OutboxRelay {
19+
20+
private final OutboxRepository outboxRepository;
21+
private final ApplicationEventPublisher eventPublisher;
22+
private final ObjectMapper objectMapper;
23+
24+
@Scheduled(fixedDelay = 60000)
25+
@Transactional
26+
public void resendPendingEvents() {
27+
List<OutboxEvent> pendingEvents = outboxRepository.findTop10ByStatusInAndRetryCountLessThanOrderByCreatedAtAsc(
28+
List.of(OutboxStatus.INIT, OutboxStatus.FAILED), 5
29+
);
30+
31+
if (pendingEvents.isEmpty()) return;
32+
33+
for (OutboxEvent outbox : pendingEvents) {
34+
try {
35+
Class<?> eventClass = Class.forName(outbox.getEventType());
36+
Object originalEvent = objectMapper.readValue(outbox.getPayload(), eventClass);
37+
38+
eventPublisher.publishEvent(originalEvent);
39+
40+
log.info("[Outbox Relay] 이벤트 재발행 성공: {}", outbox.getEventId());
41+
outbox.markPublished();
42+
43+
} catch (Exception e) {
44+
log.error("[Outbox Relay] 재발행 실패: {}", outbox.getEventId());
45+
outbox.markFailed();
46+
}
47+
}
48+
}
49+
}

apps/commerce-api/src/main/java/com/loopers/application/like/LikeCreatedEvent.java

Lines changed: 0 additions & 10 deletions
This file was deleted.

apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.loopers.application.like;
22

3+
import com.loopers.application.like.event.LikeActionTrackEvent;
4+
import com.loopers.application.like.event.LikeCreatedEvent;
35
import com.loopers.domain.like.Like;
46
import com.loopers.domain.like.LikeService;
57
import com.loopers.domain.product.Product;
@@ -29,7 +31,7 @@ public LikeInfo like(long userId, long productId) {
2931

3032
Like newLike = likeService.save(userId, productId);
3133

32-
eventPublisher.publishEvent(new LikeCreatedEvent(productId, 1));
34+
eventPublisher.publishEvent(LikeCreatedEvent.of(productId, 1));
3335

3436
eventPublisher.publishEvent(new LikeActionTrackEvent(
3537
userId,
@@ -45,7 +47,7 @@ public int unLike(long userId, long productId) {
4547

4648
likeService.unLike(userId, productId);
4749

48-
eventPublisher.publishEvent(new LikeCreatedEvent(productId, -1));
50+
eventPublisher.publishEvent(LikeCreatedEvent.of(productId, -1));
4951

5052
eventPublisher.publishEvent(new LikeActionTrackEvent(
5153
userId,

apps/commerce-api/src/main/java/com/loopers/application/like/LikeActionTrackEvent.java renamed to apps/commerce-api/src/main/java/com/loopers/application/like/event/LikeActionTrackEvent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.loopers.application.like;
1+
package com.loopers.application.like.event;
22

33
import com.loopers.domain.event.UserActionTrackEvent;
44
import java.time.ZonedDateTime;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.loopers.application.like.event;
2+
3+
import com.loopers.domain.event.DomainEvent;
4+
import java.util.UUID;
5+
6+
public record LikeCreatedEvent(
7+
String eventId,
8+
long productId,
9+
int increment,
10+
long timestamp
11+
) implements DomainEvent {
12+
13+
public static LikeCreatedEvent of(Long productId, int increment) {
14+
return new LikeCreatedEvent(
15+
UUID.randomUUID().toString(),
16+
productId,
17+
increment,
18+
System.currentTimeMillis()
19+
);
20+
}
21+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.loopers.application.like.event;
2+
3+
import com.loopers.domain.event.OutboxService;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.kafka.core.KafkaTemplate;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.transaction.event.TransactionPhase;
8+
import org.springframework.transaction.event.TransactionalEventListener;
9+
10+
@Component
11+
@RequiredArgsConstructor
12+
public class LikeEventOutboxHandler {
13+
14+
private final KafkaTemplate<Object, Object> kafkaTemplate;
15+
private final OutboxService outboxService;
16+
17+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
18+
public void handle(LikeCreatedEvent event) {
19+
outboxService.saveEvent("PRODUCT_METRICS", String.valueOf(event.productId()), event);
20+
21+
kafkaTemplate.send("catalog-events", String.valueOf(event.productId()), event)
22+
.whenComplete((result, ex) -> {
23+
if (ex == null) {
24+
outboxService.markPublished(event.eventId());
25+
} else {
26+
outboxService.markFailed(event.eventId());
27+
}
28+
});
29+
}
30+
}

apps/commerce-api/src/main/java/com/loopers/application/order/OrderCreatedEvent.java

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)