Skip to content

[Volume 7] 이벤트 기반 아키텍처 및 Kafka 파이프라인 구현 - 양권모#298

Open
Praesentia-YKM wants to merge 10 commits intoLoopers-dev-lab:Praesentia-YKMfrom
Praesentia-YKM:volume-7
Open

[Volume 7] 이벤트 기반 아키텍처 및 Kafka 파이프라인 구현 - 양권모#298
Praesentia-YKM wants to merge 10 commits intoLoopers-dev-lab:Praesentia-YKMfrom
Praesentia-YKM:volume-7

Conversation

@Praesentia-YKM
Copy link
Copy Markdown

📌 Summary

  • 배경: 기존 commerce-api는 모든 도메인 로직(좋아요 집계, 유저 행동 로깅, 알림 등)이 하나의 트랜잭션에 강결합되어 있어, 부가 로직 실패 시 핵심 비즈니스 로직까지 롤백되는 문제가 있었다. 또한 단일 프로세스 내 이벤트 처리만 존재하여, 시스템 간 이벤트 전파 및 비동기 처리가 불가능했다.
  • 목표: ApplicationEvent로 도메인 경계를 분리하고, Transactional Outbox Pattern + Kafka로 시스템 간 이벤트 파이프라인을 구축한다. 최종적으로 Kafka 기반 선착순 쿠폰 발급까지 구현한다.
  • 결과: 핵심/부가 로직 분리 완료, At Least Once 이벤트 발행 보장, Consumer 멱등 처리로 Effectively Once 달성, 선착순 쿠폰의 동시성 제어 및 비동기 발급 구현 완료.

🧭 Context & Decision

문제 정의

  • 현재 동작/제약:
    • 좋아요 토글 시 LikeTransactionService에서 좋아요 저장 + 집계 카운트 + 캐시 evict를 하나의 트랜잭션에서 처리 → Redis 장애 시 전체 롤백
    • 주문/결제 플로우에서 유저 행동 로깅이 동기 처리 → 로깅 실패가 비즈니스 로직에 영향
    • 이벤트 기반 시스템 간 통신 부재 → 집계/분석은 API 서버에서 직접 처리
    • 선착순 쿠폰 발급 시 동기 처리로 순간 트래픽에 DB 락 경쟁 심화
  • 문제(또는 리스크):
    • 부가 로직 장애가 핵심 로직 가용성을 낮춤
    • 단일 프로세스에서 모든 집계를 처리하면 API 응답 지연
    • 동기 쿠폰 발급은 동시 요청 시 DB 병목
  • 성공 기준(완료 정의):
    • 좋아요 집계 실패와 무관하게 좋아요 토글은 성공
    • 도메인 이벤트가 Kafka를 통해 commerce-streamer로 전달되고, 멱등하게 처리됨
    • 선착순 쿠폰이 수량 초과 없이 비동기 발급되고, 결과를 Polling으로 확인 가능

선택지와 결정

1. Outbox 이벤트 저장 시점

대안 설명
A. BEFORE_COMMIT (같은 TX) 도메인 변경과 outbox INSERT가 원자적으로 커밋/롤백
B. AFTER_COMMIT + 별도 TX 도메인 커밋 후 별도 TX로 outbox 저장 → 이벤트 유실 가능
  • 최종 결정: A (BEFORE_COMMIT)
  • 트레이드오프: 도메인 TX에 outbox INSERT가 포함되어 TX 시간이 미세하게 증가하지만, At Least Once 보장이 핵심이므로 감수

2. Outbox Relay 전략

대안 설명
A. 1-Phase (PENDING → PUBLISHED) 단순하지만 Kafka send()가 TX 안에 포함 → 긴 TX
B. 2-Phase (PENDING → PROCESSING → PUBLISHED) PROCESSING으로 빠르게 락 해제 후 별도로 Kafka 발행
  • 최종 결정: B (2-Phase)
  • 트레이드오프: 상태 관리가 복잡해지지만, DB 커넥션 점유 시간을 최소화. stalled PROCESSING 복구 로직 필요

3. Kafka 발행 병렬 처리

대안 설명
A. parallelStream() 간단하지만 ForkJoinPool.commonPool() 공유 → 다른 작업에 영향
B. 전용 ExecutorService 스레드 수 제어 가능, graceful shutdown 지원
  • 최종 결정: B (전용 ExecutorService, 4 threads)
  • 트레이드오프: Bean 설정이 필요하지만, Outbox Relay는 시스템 핵심 경로이므로 격리된 스레드풀로 안정성 확보

4. 선착순 쿠폰 발급 방식

대안 설명
A. 동기 발급 (API에서 직접) 즉시 결과 반환, 하지만 동시 요청 시 DB 락 경쟁 심화
B. Redis 기반 (INCR + 수량 체크) 빠르지만 Redis-DB 불일치 리스크
C. Kafka 비동기 발급 요청 버퍼링으로 DB 부하 평탄화, couponId partitionKey로 순서 보장
  • 최종 결정: C (Kafka 비동기)
  • 트레이드오프: 즉시 결과 반환 불가 → Polling API(/issue-status)로 보완. 순간 트래픽 버퍼링 + 순서 보장이라는 이점이 더 큼

5. Consumer 멱등 처리

대안 설명
A. DB event_handled 테이블 확실한 중복 방지, TX 내 원자적 체크. DB 부하 증가
B. Redis 기반 빠르지만 Redis 장애 시 중복 처리 가능
  • 최종 결정: A (DB 기반)
  • 트레이드오프: At Least Once (Outbox) + 정확한 멱등 (EventHandled) = Effectively Once 보장

추후 개선 여지:

  • Debezium CDC 기반 Outbox Relay로 폴링 제거
  • Redis 기반 멱등 캐시를 앞단에 두고 DB는 fallback으로 사용
  • WebSocket/SSE로 쿠폰 발급 결과 실시간 푸시

🏗️ Design Overview

변경 범위

  • 영향 받는 모듈/도메인: commerce-api (like, order, payment, product, coupon, outbox), commerce-streamer (consumer, metrics, coupon), modules/kafka
  • 신규 추가:
    • domain/outbox/ — OutboxEvent, OutboxStatus, OutboxRepository, OutboxEventEnvelope
    • application/outbox/ — OutboxEventListener (BEFORE_COMMIT)
    • infrastructure/outbox/ — OutboxJpaRepository, OutboxRelayService, OutboxRepositoryImpl
    • domain/*/event/ — LikeToggledEvent, OrderPlacedEvent, PaymentCompletedEvent, ProductViewedEvent, CouponIssueRequestedEvent
    • application/like/ — LikeMetricsEventListener (AFTER_COMMIT + REQUIRES_NEW)
    • application/logging/ — UserActivityEventListener (AFTER_COMMIT)
    • application/coupon/ — CouponFacade (선착순 비동기 발급)
    • config/ — KafkaTopicConfig, OutboxRelayConfig
    • commerce-streamer: CatalogEventConsumer, OrderEventConsumer, CouponIssueConsumer, ProductMetrics, EventHandled
  • 제거/대체:
    • LikeTransactionService에서 ProductService/CacheManager 직접 의존 제거 → ApplicationEventPublisher로 대체

주요 컴포넌트 책임

commerce-api (Producer 측)

컴포넌트 책임
OutboxEventListener BEFORE_COMMIT으로 도메인 이벤트를 OutboxEvent 레코드로 변환하여 DB에 저장. UUID eventId + JSON envelope 생성
OutboxRelayService @scheduled 폴링으로 PENDING 이벤트를 PROCESSING으로 전환 후, 전용 스레드풀에서 Kafka 발행. Recovery(stalled/failed) + Cleanup(published) 포함
LikeMetricsEventListener AFTER_COMMIT + REQUIRES_NEW로 좋아요 집계 처리. Redis 장애 시에도 DB 업데이트 보호 (try-catch)
UserActivityEventListener AFTER_COMMIT으로 유저 행동(주문/결제/좋아요) 로깅. 실패해도 비즈니스 로직에 영향 없음
CouponFacade 선착순 쿠폰 발급 요청의 기본 유효성 검증(만료/수량) 후 CouponIssueRequestedEvent 발행
KafkaTopicConfig catalog-events, order-events, coupon-issue-requests 토픽 자동 생성

commerce-streamer (Consumer 측)

컴포넌트 책임
CatalogEventConsumer LIKED/UNLIKED/PRODUCT_VIEWED 이벤트 → ProductMetrics 집계. @Version 낙관적 락 충돌 시 3회 재시도 + DLQ 전송
OrderEventConsumer ORDER_PLACED/PAYMENT_COMPLETED 이벤트 수신 및 로깅 + DLQ 전송
CouponIssueConsumer COUPON_ISSUE_REQUESTED → 비관적 락으로 수량 체크 + UK 중복 방지 + 발급 처리
ProductMetrics likeCount/viewCount/saleCount 집계 엔티티. @Version 낙관적 락으로 동시 업데이트 충돌 방지
EventHandled eventId PK 기반 멱등 테이블. 중복 이벤트 처리 방지

🔁 Flow Diagram

Main Flow — Transactional Outbox + Kafka Pipeline

sequenceDiagram
    autonumber
    participant Client
    participant API as commerce-api
    participant DB as MySQL
    participant Relay as OutboxRelayService
    participant Kafka
    participant Streamer as commerce-streamer

    Client->>API: POST /api/v1/likes (좋아요 토글)
    API->>DB: INSERT like + INSERT outbox_event (같은 TX, BEFORE_COMMIT)
    DB-->>API: commit
    API-->>Client: 200 OK

    Note over API: AFTER_COMMIT → LikeMetricsEventListener<br/>(REQUIRES_NEW TX로 집계)

    loop @Scheduled(fixedDelay=1000)
        Relay->>DB: SELECT outbox WHERE status=PENDING<br/>FOR UPDATE SKIP LOCKED
        Relay->>DB: UPDATE status=PROCESSING (Phase 1)
        Relay->>Kafka: send(topic, key, payload, headers) (Phase 2)
        alt 발행 성공
            Relay->>DB: UPDATE status=PUBLISHED
        else 발행 실패
            Relay->>DB: UPDATE status=FAILED, retryCount++
        end
    end

    Kafka->>Streamer: consume(catalog-events)
    Streamer->>DB: EventHandled 멱등 체크
    alt 미처리 이벤트
        Streamer->>DB: ProductMetrics upsert (좋아요/조회수/판매량)
        Streamer->>DB: INSERT event_handled
    else 이미 처리됨
        Note over Streamer: skip (멱등)
    end
Loading

Main Flow — 선착순 쿠폰 발급

sequenceDiagram
    autonumber
    participant Client
    participant API as commerce-api
    participant DB as MySQL
    participant Relay as OutboxRelayService
    participant Kafka
    participant Consumer as CouponIssueConsumer

    Client->>API: POST /coupons/{id}/issue-async
    API->>API: 만료/수량 기본 검증
    API->>DB: INSERT outbox_event (BEFORE_COMMIT)
    API-->>Client: 200 OK (요청 접수)

    Relay->>Kafka: send(coupon-issue-requests, couponId)

    Kafka->>Consumer: consume
    Consumer->>DB: SELECT coupon FOR UPDATE (비관적 락)
    Consumer->>DB: 만료/수량/중복 체크
    alt 발급 가능
        Consumer->>DB: INSERT coupon_issue + UPDATE issued_count
        Consumer-->>DB: event_handled 저장
    else 수량 초과 or 중복
        Consumer-->>DB: event_handled 저장 (skip)
    end

    Client->>API: GET /coupons/{id}/issue-status (Polling)
    API->>DB: SELECT coupon_issue WHERE user+coupon
    alt 발급 완료
        API-->>Client: status ISSUED, issue details
    else 아직 처리 안 됨
        API-->>Client: status PENDING
    end
Loading

Exception Flow — DLQ + Recovery

flowchart TB
    subgraph Relay Recovery
        A[PROCESSING > 5분] -->|recoverStalledEvents| B[PENDING으로 복구]
        C[FAILED + retryCount < 5] -->|retryFailedEvents| B
        D[PUBLISHED > 1시간] -->|cleanupPublishedEvents| E[DELETE]
    end

    subgraph Consumer DLQ
        F[처리 실패] --> G{재시도 가능?}
        G -->|Version 충돌| H[최대 3회 재시도]
        H -->|3회 초과| I[DLQ 토픽 전송]
        G -->|기타 에러| I
        I --> J[catalog-events.DLT / order-events.DLT]
    end
Loading

Kafka Header 구조

필드 예시 용도
X-Event-Id 550e8400-e29b-41d4-a716-446655440000 Consumer 멱등 체크
X-Event-Type LIKED / ORDER_PLACED / COUPON_ISSUE_REQUESTED payload 파싱 없이 라우팅
X-Aggregate-Type Product / Order / Payment / Coupon 집계 대상 식별
Key (partitionKey) "100" (productId / orderId / couponId) 같은 엔티티 이벤트 순서 보장

Praesentia-YKM and others added 10 commits March 11, 2026 12:36
- ProductService: getById() → getProduct() 리네임, getProductForAdmin() 추가
- ProductSortType: LATEST 값 추가, ProductRepositoryImpl에 LATEST 케이스 처리
- BrandFacade: deleteAllByBrandId() → softDeleteByBrandId() 호출 수정
- ProductRepositoryImpl: 중복 save() 메서드 제거
- 테스트 5개 파일: 메서드명 불일치 일괄 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ProductFacade에 @Cacheable/@CacheEvict 적용 (상품 상세 캐시)
- LikeTransactionService에 좋아요 변경 시 캐시 무효화 추가
- RedisCacheConfig, CustomCacheErrorHandler 추가
- 고아 클래스 ProductListCacheService 삭제
- 중복 admin/ 패키지 및 interfaces/auth/ 패키지 정리
- import 경로 수정 및 getter 이름 불일치 해결
- 캐시 통합 테스트 추가 (ProductCacheIntegrationTest, LikeTransactionCacheTest)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- QueryDSL BooleanBuilder로 brandId 동적 필터 + 정렬(좋아요순 등) 구현
- 복합 인덱스 4개 추가 (brand+deleted+like, deleted+like, deleted+created, deleted+price)
- N+1 문제 해결: 목록 조회 시 배치 쿼리 패턴(Map<Long, Model>) 적용
- likes 테이블 유니크 제약조건 추가 (uk_likes_user_product)
- StockService 메서드 참조 오류 수정 (productId → getProductId)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts:
#	apps/commerce-api/src/main/java/com/loopers/application/brand/BrandService.java
#	apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
#	apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
#	apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeTest.java
#	apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeTest.java
- Payment 도메인 전체 레이어 구현 (Model, Repository, Service, Facade, Controller)
- PG 클라이언트 인프라 구성 (PgClient, PgProperties, PgClientConfig)
- Order 도메인에 결제 관련 상태 및 로직 추가
- ErrorType에 결제 관련 에러 코드 추가
- 설계 문서 업데이트 (클래스 다이어그램, ERD, 시퀀스, 상태 다이어그램)
- 단위/통합 테스트 작성 (PaymentModelTest, PaymentFacadeTest)
- API 테스트용 .http 파일 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Resilience4j 적용 (CircuitBreaker + Retry + Bulkhead)
- TX 분리 패턴으로 PG 호출 동안 DB 커넥션 미점유
- Polling 스케줄러로 PENDING 결제 자동 복구 (60초 주기, fail-fast)
- 고아 Payment 복구 (orderId 기반 Phase C)
- WireMock 기반 PG 연동 통합 테스트 9종
- resilience4j-lab 분석 기반 설정 함정 수정

🤖 Generated with Claude Code
- 좋아요/주문/결제 도메인에 이벤트 record 정의 (LikeToggledEvent, OrderPlacedEvent, PaymentCompletedEvent)
- LikeTransactionService에서 직접 호출 대신 이벤트 발행으로 전환
- LikeMetricsEventListener: AFTER_COMMIT + REQUIRES_NEW로 좋아요 집계 처리
- OrderFacade.placeOrder(), PaymentFacade.handleCallback()에서 이벤트 발행
- UserActivityEventListener: 모든 도메인 이벤트를 구독하여 유저 행동 로깅
- 캐시 evict 실패 시에도 DB 업데이트가 롤백되지 않도록 try-catch 처리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Step 2: Transactional Outbox Pattern + Kafka Producer/Consumer
- OutboxEvent 엔티티 (2-Phase: PENDING → PROCESSING → PUBLISHED/FAILED)
- OutboxEventListener (BEFORE_COMMIT으로 도메인 TX 원자성 보장)
- OutboxRelayService (전용 ExecutorService + @scheduled 폴링 + Recovery/Cleanup)
- ProductViewedEvent 추가 및 ProductFacade 연동
- Kafka 설정: acks=all, idempotence=true
- CatalogEventConsumer (LIKED/UNLIKED/PRODUCT_VIEWED → ProductMetrics 집계)
- OrderEventConsumer (ORDER_PLACED/PAYMENT_COMPLETED 로깅)
- EventHandled 테이블 기반 Consumer 멱등 처리
- @Version 낙관적 락 충돌 시 재시도 + DLQ 전송

Step 3: Kafka 기반 선착순 쿠폰 발급
- CouponModel에 maxQuantity/issuedCount 추가
- CouponFacade.requestCouponIssue() → Kafka 비동기 위임
- CouponIssueConsumer (비관적 락 + 수량 제한 + UK 중복 방지)
- 발급 결과 확인 Polling API (/issue-status)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Important

Review skipped

Too many files!

This PR contains 154 files, which is 4 over the limit of 150.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14c76f78-7dd4-45d7-bc27-d536c3d1cb71

📥 Commits

Reviewing files that changed from the base of the PR and between ab5f265 and 2167c3b.

⛔ Files ignored due to path filters (22)
  • .claude/commands/analyze-transaction.md is excluded by !**/*.md and included by **
  • docs/blog/index-tuning-blog-draft.md is excluded by !**/*.md and included by **
  • docs/blog/index-tuning-research.md is excluded by !**/*.md and included by **
  • docs/design/mermaid/00-ddd-design-framework.md is excluded by !**/*.md and included by **
  • docs/design/mermaid/01-requirements.md is excluded by !**/*.md and included by **
  • docs/design/mermaid/02-ubiquitous-language.md is excluded by !**/*.md and included by **
  • docs/plans/2026-03-12-query-optimization.md is excluded by !**/*.md and included by **
  • docs/plans/2026-03-12-redis-cache.md is excluded by !**/*.md and included by **
  • docs/plans/2026-03-19-payment-domain.md is excluded by !**/*.md and included by **
  • docs/plans/2026-03-20-resilience4j-design.md is excluded by !**/*.md and included by **
  • docs/plans/2026-03-27-step1-application-event.md is excluded by !**/*.md and included by **
  • docs/requirements/round5-requirement.md is excluded by !**/*.md and included by **
  • docs/requirements/round6-requirement.md is excluded by !**/*.md and included by **
  • docs/requirements/round7-requirement.md is excluded by !**/*.md and included by **
  • docs/research.md is excluded by !**/*.md and included by **
  • docs/week6/01-pg-simulator-setup/research.md is excluded by !**/*.md and included by **
  • docs/week6/02-payment-domain/research.md is excluded by !**/*.md and included by **
  • docs/week6/03-pg-client-integration/research.md is excluded by !**/*.md and included by **
  • docs/week6/04-resilience/research.md is excluded by !**/*.md and included by **
  • docs/나만의 동시성 제어 판단기준.png is excluded by !**/*.png, !**/*.png and included by **
  • pr-body.md is excluded by !**/*.md and included by **
  • research.md is excluded by !**/*.md and included by **
📒 Files selected for processing (154)
  • .http/cache-test.http
  • .http/payment.http
  • apps/commerce-api/build.gradle.kts
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandService.java
  • apps/commerce-api/src/main/java/com/loopers/application/coupon/CouponFacade.java
  • apps/commerce-api/src/main/java/com/loopers/application/coupon/CouponIssueService.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeMetricsEventListener.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeTransactionService.java
  • apps/commerce-api/src/main/java/com/loopers/application/logging/UserActivityEventListener.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/OrderService.java
  • apps/commerce-api/src/main/java/com/loopers/application/outbox/OutboxEventListener.java
  • apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentCommand.java
  • apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentFacade.java
  • apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentInfo.java
  • apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentPollingScheduler.java
  • apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentService.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/ProductDetail.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/ProductService.java
  • apps/commerce-api/src/main/java/com/loopers/application/stock/StockService.java
  • apps/commerce-api/src/main/java/com/loopers/config/KafkaTopicConfig.java
  • apps/commerce-api/src/main/java/com/loopers/config/OutboxRelayConfig.java
  • apps/commerce-api/src/main/java/com/loopers/config/PgClientConfig.java
  • apps/commerce-api/src/main/java/com/loopers/config/PgProperties.java
  • apps/commerce-api/src/main/java/com/loopers/config/SchedulerConfig.java
  • apps/commerce-api/src/main/java/com/loopers/config/WebMvcConfig.java
  • apps/commerce-api/src/main/java/com/loopers/domain/brand/BrandRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponModel.java
  • apps/commerce-api/src/main/java/com/loopers/domain/coupon/event/CouponIssueRequestedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/like/LikeModel.java
  • apps/commerce-api/src/main/java/com/loopers/domain/like/event/LikeToggledEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/order/OrderModel.java
  • apps/commerce-api/src/main/java/com/loopers/domain/order/OrderStatus.java
  • apps/commerce-api/src/main/java/com/loopers/domain/order/event/OrderPlacedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/outbox/OutboxEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/outbox/OutboxEventEnvelope.java
  • apps/commerce-api/src/main/java/com/loopers/domain/outbox/OutboxRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/outbox/OutboxStatus.java
  • apps/commerce-api/src/main/java/com/loopers/domain/payment/CardType.java
  • apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentModel.java
  • apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentStatus.java
  • apps/commerce-api/src/main/java/com/loopers/domain/payment/event/PaymentCompletedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/product/Money.java
  • apps/commerce-api/src/main/java/com/loopers/domain/product/ProductModel.java
  • apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/product/ProductSortType.java
  • apps/commerce-api/src/main/java/com/loopers/domain/product/event/ProductViewedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/stock/StockRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/outbox/OutboxJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/outbox/OutboxRelayService.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/outbox/OutboxRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PaymentJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PaymentRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PgClient.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PgPaymentGateway.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/dto/PgPaymentRequest.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/dto/PgPaymentResponse.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/stock/StockRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/auth/LoginMemberArgumentResolver.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/BrandAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/admin/BrandAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/admin/BrandAdminV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/coupon/CouponAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/coupon/CouponV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/coupon/CouponV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/member/MemberV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderAdminV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1ApiSpec.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/admin/OrderAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/admin/OrderAdminV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/payment/PaymentV1ApiSpec.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/payment/PaymentV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/payment/PaymentV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductAdminV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/admin/ProductAdminV1Controller.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/admin/ProductAdminV1Dto.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/auth/AdminInfo.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/auth/AdminUser.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/auth/AdminUserArgumentResolver.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/auth/LoginMember.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/auth/LoginMemberArgumentResolver.java
  • apps/commerce-api/src/main/java/com/loopers/support/error/ErrorType.java
  • apps/commerce-api/src/main/resources/application.yml
  • apps/commerce-api/src/test/java/com/loopers/application/ConcurrencyIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/brand/BrandFacadeTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/brand/BrandServiceIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/brand/BrandServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeMetricsEventListenerTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeTransactionCacheTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeTransactionServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/logging/UserActivityEventListenerTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/order/OrderFacadeTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/outbox/OutboxEventListenerTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/payment/PaymentFacadeIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/payment/PaymentFacadeTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/product/ProductCacheIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/product/ProductFacadeTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/product/ProductServiceIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/product/ProductServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/stock/StockServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/config/CustomCacheErrorHandlerTest.java
  • apps/commerce-api/src/test/java/com/loopers/config/RedisCacheConfigTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponModelQuantityTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/like/LikeServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/like/event/LikeToggledEventTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/order/OrderModelTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/outbox/OutboxEventTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/payment/PaymentModelTest.java
  • apps/commerce-api/src/test/java/com/loopers/infrastructure/payment/PgClientWireMockTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/brand/BrandV1ApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/like/LikeV1ApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/order/OrderV1ApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/product/ProductV1ApiE2ETest.java
  • apps/commerce-streamer/src/main/java/com/loopers/domain/coupon/CouponEntity.java
  • apps/commerce-streamer/src/main/java/com/loopers/domain/coupon/CouponIssueEntity.java
  • apps/commerce-streamer/src/main/java/com/loopers/domain/idempotency/EventHandled.java
  • apps/commerce-streamer/src/main/java/com/loopers/domain/metrics/ProductMetrics.java
  • apps/commerce-streamer/src/main/java/com/loopers/infrastructure/coupon/CouponIssueJpaRepository.java
  • apps/commerce-streamer/src/main/java/com/loopers/infrastructure/coupon/CouponJpaRepository.java
  • apps/commerce-streamer/src/main/java/com/loopers/infrastructure/idempotency/EventHandledJpaRepository.java
  • apps/commerce-streamer/src/main/java/com/loopers/infrastructure/metrics/ProductMetricsJpaRepository.java
  • apps/commerce-streamer/src/main/java/com/loopers/interfaces/consumer/CatalogEventConsumer.java
  • apps/commerce-streamer/src/main/java/com/loopers/interfaces/consumer/CouponIssueConsumer.java
  • apps/commerce-streamer/src/main/java/com/loopers/interfaces/consumer/OrderEventConsumer.java
  • apps/commerce-streamer/src/test/java/com/loopers/domain/metrics/ProductMetricsTest.java
  • docs/design/mermaid/03-sequence-payment-callback.mmd
  • docs/design/mermaid/03-sequence-payment-polling.mmd
  • docs/design/mermaid/03-sequence-payment-request.mmd
  • docs/design/mermaid/04-class-diagram.mmd
  • docs/design/mermaid/05-erd.mmd
  • docs/design/mermaid/06-payment-state-diagram.mmd
  • docs/mentoring
  • docs/sql/seed-data.sql
  • modules/kafka/src/main/resources/kafka.yml
  • modules/redis/src/main/java/com/loopers/config/redis/CustomCacheErrorHandler.java
  • modules/redis/src/main/java/com/loopers/config/redis/RedisCacheConfig.java

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant