diff --git a/docs/erd-diagram.md b/docs/erd-diagram.md new file mode 100644 index 0000000..e3369d9 --- /dev/null +++ b/docs/erd-diagram.md @@ -0,0 +1,89 @@ +```mermaid +erDiagram + USERS { + BIGINT id PK + DATETIME created_at "생성일자" + } + + WALLET { + BIGINT id PK + BIGINT user_id FK "유저 식별자" + BIGINT balance "잔액" + DATETIME created_at "생성일자" + DATETIME updated_at "수정일시" + } + + USER_POINT_HISTORY { + BIGINT id PK + BIGINT user_id FK "유저 식별자" + BIGINT wallet_id FK "지갑 식별자" + BIGINT amount "금액" + STRING type "CHARGE, USE" + DATETIME created_at "생성일자" + } + + PRODUCT { + BIGINT id PK + STRING name "상품이름" + BIGINT price "금액" + INT stock_quantity "수량" + DATETIME created_at "생성일시" + DATETIME update_at "수정일시" + } + + ORDER { + BIGINT id PK + STRING nonce UK "주문키" + BIGINT user_id FK "유저식별자" + BIGINT total_amount "주문금액" + BIGINT discount_amount "할인 금액" + BIGINT paid_amount "지불 금액" + DATETIME created_at "생성일시" + } + + ORDER_ITEM { + BIGINT id PK + BIGINT order_id FK "주문식별자" + BIGINT product_id FK "상품식별자" + BIGINT unit_price "상품 가격" + INT quantity "주문수량" + BIGINT total_price "주문금액" + DATETIME created_at "생성일시" + } + + COUPON { + BIGINT id PK + STRING name "쿠폰이름" + BIGINT discount_amount "할인금액" + STRING discount_type "PERCENT, AMOUNT" + BIGINT days "발급후 사용 가능 일자" + INT total_quantity "총 수량" + DATETIME created_at "생성일시" + } + + COUPON_OWNED { + BIGINT id PK + BIGINT user_id FK "유저식별자" + BIGINT coupon_id FK "쿠폰식별자" + BIGINT discount_amount "할인금액" + STRING discount_type "PERCENT, AMOUNT" + DATETIME issued_at "발급일" + DATETIME started_at "사용시작" + DATETIME end_at "사용마감" + STRING state "ISSUED, USED" + BIGINT order_id FK "사용한 주문식별자" + } + +%% 관계 정의 + USERS ||--|| WALLET : "보유한다" + USERS ||--o{ ORDER : "주문한다" + USERS ||--o{ USER_POINT_HISTORY : "충전/사용한다" + WALLET ||--o{ USER_POINT_HISTORY : "충전/사용한다" + USERS ||--o{ COUPON_OWNED : "쿠폰보유" + + ORDER ||--o{ ORDER_ITEM : "포함한다" + PRODUCT ||--o{ ORDER_ITEM : "구성된다" + + COUPON ||--o{ COUPON_OWNED : "발급된다" + COUPON_OWNED ||--|| ORDER : "적용된다" +``` \ No newline at end of file diff --git a/docs/requirements.md b/docs/requirements.md new file mode 100644 index 0000000..1de085e --- /dev/null +++ b/docs/requirements.md @@ -0,0 +1,107 @@ +## 이커머스 도메인 분석 + +--- +### 주체 + +| 주체 | 역할 | +| ----------- |---------------------------------| +| **사용자(고객)** | 상품을 보고, 결제함 | +| 판매자(또는 관리자) | 상품 등록, 재고 관리, 주문 확인 | +| **시스템** | 상품 노출, 주문/결제 처리, 재고 차감, 쿠폰 발급 등 | + +- 이번 프로젝트는 동시성과 TDD에 집중하기 위함으로 **판매자 주체는 크게 고려하지 않는다.** + +### 도메인 주요 고려 사항 +- **이벤트로 인해 트래픽 폭주 상황이 발생할 우려** + - → 서버 수평 확장성 고려 필요 + - → 빠른 트랜잭션 처리 고려 필요 + +## API 고려 사항 정리 + +--- + +## 1. 잔액 +### 1.1. 잔액 조회 API +#### 1.1.1. **기능적 요구사항** +- 사용자는 사용자 식별값을 보내어 자신의 잔액을 조회할 수 있다. +- 사용자가 유효하지 않으면 오류를 반환한다. +- 사용자는 반드시 하나의 지갑을 가지고 있다. +- 포인트 정보가 없으면 오류를 반환한다. + +### 1.2. 잔액 충전 API +#### 1.2.1. **기능적 요구사항** +- 사용자는 사용자 식별값과 충전금액을 보내어 자신의 잔액을 충전할 수 있다. +- 충전 전 요청에 대한 유효성을 검증해야한다. + - 유효하지 않은 유저라면 오류를 반환한다. + - 충전 정책을 만족하지 않으면 오류를 반환한다. +- 충전에 성공하면 충전 후 사용자에게 잔액정보(`사용자ID`, `잔액`)를 응답한다. +- 충전에 성공하면 충전에 대한 로그를 저장한다. +- 충전 정책 + - 최소 충전 금액: 1,000원 + +#### 1.2.2. **비기능적 요구사항** +- 성능: 로그 적재는 충전 트랜잭션에 포함하지 않고, 비동기로 요청하여 성능을 올린다. +- 동시성: 다수의 요청이 동시에 들어와도 충전 금액이 정확하게 반영되도록 처리해야 한다. +- 멱등성: 사용자에게 동일한 충전 요청이 여러번 들어와도, 같은 충전 요청임을 확인할 수 있어야한다. +- 정합성: 충전에 대한 처리와 로그가 일치하게 적재되어야한다. + +## 2. 상품 +### 2.1. 상품조회 API +#### 2.1.1 **기능적 요구사항** +- 사용자는 인증 없이 상품을 조회할 수 있다. +- 조회되는 상품이 없으면 오류를 반환한다. +- 조회되는 상품이 있으면 상품 정보(`상품ID`,`상품이름`, `가격`, `수량`)을 응답한다. + +## 3. 주문/결제 +### 3.1. 주문/결제 API +#### 3.1.1 **기능적 요구사항** +- 사용자는 사용자 식별값을 보내어 주문 요청을 보낼 수 있다. +- 주문 당 쿠폰 1개만 사용 가능하다. +- 결제 전 요청을 검증하여 유효성을 판단해야한다. + - 유효하지 않은 유저라면 오류를 반환한다. + - 사용자의 잔액이 부족한 경우 주문은 오류를 반환한다. + - 상품의 수량이 주문 수량보다 적은 경우 주문은 오류를 반환한다. +- 사용자가 쿠폰 정보를 보낸다면 결제 금액을 계산할 떄 적용되어야한다. +- 쿠폰은 유효성 검사는 아래 내용이 포함된다. + - 쿠폰 사용자 + - 쿠폰 사용 기간 +- 주문/결제가 성공하면 사용자에게 결과정보(`주문ID`, `결제금액`, `할인금액`, `지불금액`, `생성일시`)을 전달한다. +- 주문/결제가 성공하면 포인트 사용에 대한 로그를 저장되어야한다. + + +#### 3.1.2 **비기능적 요구사항** +- 원자성: 요청 처리 중 오류가 발생하면 요청 전 상태로 돌아가야한다. +- 동시성: 주문 과정에서 쿠폰 사용, 잔액과 재고 차감, 롤백 중에 레이스 컨디션이 발생하지 않아야한다. +- 안정성: 요청된 결제 정보는 네트워크 오류 등의 문제로 누락되지 않아야한다. +- 성능: 로그 적재는 충전 트랜잭션에 포함하지 않고, 비동기로 요청하여 성능을 올린다. +- 멱등성: 사용자에게 동일한 사용 요청이 여러번 들어와도, 같은 사용 요청임을 확인할 수 있어야한다. +- 정합성: 사용에 대한 처리와 로그가 일치하게 적재되어야한다. + + +## 4. 쿠폰 +### 4.1. 선착순 쿠폰 발급 API +#### 4.1.1 **기능적 요구사항** +- 사용자는 사용자 식별값과 발급받을 쿠폰 정보를 보내어 쿠폰을 발급받을 수 있다. +- 쿠폰은 발급 수량이 제한된다. +- 선착순 방식으로 제공된다. +- 쿠폰 발급 전 유효한 요청인지 검증해야한다. + - 유효한 사용자가 아니면 오류를 반환한다. + - 요청된 쿠폰 정보가 존재하지 않으면 오류를 반환한다. + - 쿠폰 수량을 초과하면 오류를 반환한다. + - 이미 해당 쿠폰을 발급된 유저라면 오류를 반환한다. +- 유효한 요청이면 발급된 쿠폰 정보를 응답한다. + + +#### 4.1.2 **비기능적 요구사항** +- 동시성: 쿠폰 수량 차감은 중 레이스 컨디션이 발생하지 않아야한다. +- 성능: 선착순 쿠폰이므로 큰 트래픽을 견뎌야한다. +- 정합성: 쿠폰이 발급 응답과 저장된 데이터가 일치해야한다. + +## 5. 인기 상품 조회 +### 5.1. 인기 판매 상품 조회 API +#### 5.1.1 **기능적 요구사항** +- 사용자는 인증 없이 최근 3일간 가장 많이 팔린 상위 5개 상품 정보를 조회할 수 있다. +- 해당 상품을 주문한 횟수를 고려한다 (수량 고려 X) +- 정보가 없으면 빈 값을 응답한다. +#### 5.1.2 **비기능적 요구사항** +- 성능: 다수의 사용자가 동시에 조회할 수 있으므로 조회 속도를 고려한다. diff --git a/docs/sequence-diagram.md b/docs/sequence-diagram.md new file mode 100644 index 0000000..6fc1b84 --- /dev/null +++ b/docs/sequence-diagram.md @@ -0,0 +1,255 @@ +# 0. 공통 멱등성 처리 +## 0.1. 중복 요청이 아닌 경우 +```mermaid +sequenceDiagram + participant Client + participant AOP + participant 인메모리DB + participant Controller + participant Service + + Client->>AOP: 요청 전송 (nonce 포함) + AOP->>AOP: 데이터 형식 검증 + AOP->>인메모리DB: userId:endpoint:nonce 조회 + + 인메모리DB-->>AOP: 키 없음 + AOP->>인메모리DB: 상태 저장 (PROCESSING, TTL=10s) + AOP->>Service: 실제 비즈니스 로직 실행 + Service-->>AOP: 처리 결과 반환 + AOP->>인메모리DB: 상태 업데이트 (COMPLETED, 결과 저장) + AOP-->>Controller: 정상 응답 + + Controller-->>Client: 최종 응답 +``` +## 0.2. 중복 요청인 경우 +```mermaid +sequenceDiagram + participant Client + participant AOP + participant 인메모리DB + participant Controller + participant Service + + Client->>AOP: 요청 전송 (nonce 포함) + AOP->>AOP: 데이터 형식 검증 + AOP->>인메모리DB: userId:endpoint:nonce 조회 + + 인메모리DB-->>AOP: 키 있음 + alt 상태가 PROCESSING + 인메모리DB-->>AOP: PROCESSING + AOP-->>Controller: "요청 처리 중" 응답 + else 상태가 COMPLETED + 인메모리DB-->>AOP: COMPLETED + 결과 + AOP-->>Controller: 이전 응답 재전송 + end + Controller-->>Client: 최종 응답 +``` + +# 1. 잔액충전 API +## 1.1 잔액 충전 성공 +```mermaid +sequenceDiagram + actor 사용자 + participant 포인트API + participant 포인트 + participant 충전이벤트큐 + participant 충전로그 + + 사용자->>포인트API: 포인트 충전 요청 (금액) + + note right of 포인트API: 트랜잭션 시작 + activate 포인트API + + 포인트API->>포인트: 충전 요청 + activate 포인트 + 포인트->>포인트: 금액 증가 및 유효성 검사 + 포인트-->>포인트API: 충전 완료 + deactivate 포인트 + note right of 포인트API: 트랜잭션 끝 + + 포인트API->>충전이벤트큐: 충전 완료 이벤트 발행 + 포인트API-->>사용자: 충전 성공 응답 + deactivate 포인트API + + 충전이벤트큐-->>충전로그: 충전 이벤트 수신 + activate 충전로그 + 충전로그->>충전로그: 충전 로그 저장 + deactivate 충전로그 +``` + +# 2. 상품조회 API +```mermaid +sequenceDiagram + actor 사용자 + participant 상품API + participant 상품 + + 사용자->>상품API: 상품 ID로 조회 요청 + activate 상품API + + 상품API->>상품: ID로 상품 탐색 요청 + activate 상품 + + 상품->>상품: 조회 + + 상품-->>상품API: 상품 응답 반환 + 상품API-->>사용자: 상품 상세 응답 + + deactivate 상품 + deactivate 상품API + +``` + +# 3. 주문/결제 API +```mermaid +sequenceDiagram + actor 사용자 + participant 주문 + participant 쿠폰 + participant 상품 + participant 포인트 + participant 대기열 + participant 통계플랫폼 + + 사용자->>주문: 주문 요청 + + activate 주문 + note right of 주문: 트랜잭션 시작 + + 주문->>쿠폰: 사용 요청 + activate 쿠폰 + 쿠폰->>쿠폰: 유효성 검증 및 사용 처리 + 쿠폰-->>주문: 사용 완료 + deactivate 쿠폰 + + 주문->>상품: 수량 확인 및 차감 요청 + activate 상품 + 상품->>상품: 재고 수량 확인 및 차감 + 상품-->>주문: 차감 완료 + deactivate 상품 + + 주문->>포인트: 포인트 차감 요청 + activate 포인트 + 포인트->>포인트: 유효성 검증 및 차감 처리 + 포인트-->>주문: 차감 완료 + deactivate 포인트 + + note right of 주문:: 트랜잭션 끝 + + 주문->>대기열: 주문 통계 이벤트 발행 + 주문-->>사용자: 주문 성공 응답 + + deactivate 주문 + + 대기열-->>통계플랫폼: 주문 통계 이벤트 수신 +``` +## 3.1. 쿠폰 유효성 검증 실패 예외 +```mermaid +sequenceDiagram + actor 사용자 + participant 주문 + participant 쿠폰 + + 사용자->>주문: 주문 요청 + activate 주문 + + 주문->>쿠폰: 사용 요청 + 쿠폰->>쿠폰: 유효성 검사 + + 쿠폰-->>주문: 예외 발생(쿠폰 사용자/쿠폰 사용 기간 불일치) + + 주문-->>사용자: 400 Bad Request (유효성 검증 실패) + deactivate 주문 +``` + +## 3.2. 상품 유효성 검증 실패 +```mermaid +sequenceDiagram + actor 사용자 + participant 주문 + participant 상품 + + 사용자->>주문: 주문 요청 (상품ID, 수량, 쿠폰ID 등) + activate 주문 + + + 주문->>상품: 수량 확인 및 차감 요청 + 상품->>상품: 유효성 검사 및 차감 + 상품-->>주문: 예외 발생 (재고 부족) + + note right of 주문: 트랜잭션 롤백됨
→ 쿠폰사용/상품차감 취소됨 + + 주문-->>사용자: 400 Bad Request (재고 부족) + deactivate 주문 +``` + +## 3.3. 포인트 잔액 부족 +```mermaid +sequenceDiagram + actor 사용자 + participant 주문 + participant 포인트 + + 사용자->>주문: 주문 요청 + 주문->>포인트: 포인트 차감 요청 + 포인트-->>포인트: 유효성 검증 + 포인트-->>주문: 예외 발생 (잔액 부족) + note right of 주문: 전체 트랜잭션 롤백
→ 쿠폰사용/상품차감/포인트차감 취소됨 + 주문-->>사용자: 400 Bad Request (포인트 부족) +``` + +# 4. 선착순 쿠폰 발급 API +```mermaid +sequenceDiagram + actor 사용자 + participant API서버 + participant 인메모리DB + participant 대기열 + participant 쿠폰 + participant 데이터 플랫폼 + + 사용자->>API서버: 쿠폰 발급 요청 + activate API서버 + + API서버->>인메모리DB: 쿠폰 수량 차감 + 인메모리DB-->>API서버: 남은 수량 반환 + + API서버-->>사용자: 발급 성공 응답 + API서버->>대기열: 발급 이벤트 전송 + deactivate API서버 + + activate 대기열 + 대기열-->>쿠폰: 발급 이벤트 전달 + deactivate 대기열 + + activate 쿠폰 + 쿠폰->>데이터 플랫폼: 발급 이력 저장 + deactivate 쿠폰 + +``` + +# 5. 인기 판매 상품 조회 API +```mermaid +sequenceDiagram + actor 사용자 + participant 인기상품API + participant 인메모리DB + participant 주문상품 + participant 인기상품스케줄러 + + Note over 인기상품스케줄러: 1시간마다 실행 + + activate 인기상품스케줄러 + 인기상품스케줄러->>주문상품: 최근 3일간 주문 항목 조회 + 주문상품-->>인기상품스케줄러: 상품별 판매 수량 집계 + 인기상품스케줄러->>인메모리DB: 인기 상품 Top 5 캐시 저장 + deactivate 인기상품스케줄러 + + 사용자->>인기상품API: 인기 상품 조회 요청 + activate 인기상품API + 인기상품API->>인메모리DB: 인기 상품 캐시 조회 + 인메모리DB-->>인기상품API: Top 5 목록 반환 + 인기상품API-->>사용자: 인기 상품 응답 + deactivate 인기상품API + +``` \ No newline at end of file