-
Notifications
You must be signed in to change notification settings - Fork 35
상품, 브랜드, 좋아요, 주문 도메인 구현 #76
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
Open
leeminkyu-kr96
wants to merge
11
commits into
Loopers-dev-lab:main
Choose a base branch
from
leeminkyu-kr96:feature/week3-set-domain-layer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
369c10d
git 이전
minkyuLee-dev96 5974fe4
설계문서 작성
minkyuLee-dev96 2d743ab
유저, 포인트 도메인 VO 생성 및 테스트 코드 수정
minkyuLee-dev96 710e8dc
상품 목록/상세 조회 기능 추가 및 포인트, 재고 차감 기능 추가가
minkyuLee-dev96 fd6a584
상품 좋아요 등록/취소/멱등성 검증 기능 추가
minkyuLee-dev96 ac7242e
주문 기능 추가
minkyuLee-dev96 c503e64
브랜드 조회 기능 추가가
minkyuLee-dev96 2ff2147
단위 테스트 진행
minkyuLee-dev96 dcf70d2
유저 및 포인트 VO 추가 및 repository 수정정
minkyuLee-dev96 27a4b1e
VO recode 에러로 인한 임시 class로 변경 및 단위테스트 수정정
minkyuLee-dev96 0ce5fe1
[코드래빗 수정 요청 사안 수정]
minkyuLee-dev96 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| # **요구사항 명세서** | ||
|
|
||
| ## **1. 상품 목록 조회** | ||
|
|
||
| ### 1.1. 유저 시나리오 | ||
| > "사용자가 상품을 둘러보기 위해 사이트에 접속했다. 그는 어떤 제품이 인기가 많은지 보기 위해 상품 목록을 조회한다." | ||
|
|
||
| ### 1.2. 핵심 기능 정의 | ||
| - **F-1:** 상품 목록을 조회할 수 있다. | ||
| - **F-2:** 상품 목록을 '좋아요순'으로 정렬할 수 있다. | ||
| - **F-3:** 상품 목록을 '최신순', '가격순' 등 다른 기준으로도 정렬할 수 있다. | ||
| - **F-4:** 상품 목록을 페이지 단위로 나누어 볼 수 있다. | ||
|
|
||
| ### 1.3. 유스케이스 흐름 | ||
| * **Main Flow (기본 조회):** | ||
| 1. 사용자가 상품 목록 페이지에 진입한다. | ||
| 2. 최신순으로 첫 번째 페이지의 상품 목록을 반환한다. | ||
| 3. 화면에 상품 목록이 표시된다. | ||
| * **Alternate Flow (정렬 변경):** | ||
| 1. 사용자가 '인기순' 정렬 버튼을 클릭한다. | ||
| 2. 인기순으로 상품 목록을 다시 조회하여 반환한다. | ||
| 3. 화면에 인기순으로 정렬된 목록이 표시된다. | ||
| * **Alternate Flow (페이징):** | ||
| 1. 사용자가 다른 페이지를 클릭한다. | ||
| 2. 다른 페이지의 상품 목록을 조회하여 반환한다. | ||
| 3. 화면에 다음 상품 목록이 이어서 표시된다. | ||
| * **Exception Flow (결과 없음):** | ||
| 1. 사용자가 특정 필터를 적용했으나, 해당하는 상품이 하나도 없다. | ||
| 2. "조회된 상품이 없습니다."라는 메시지를 반환한다. | ||
|
|
||
| --- | ||
|
|
||
| ## **2. 상품 상세 조회** | ||
|
|
||
| ### 2.1. 유저 시나리오 | ||
| > "사용자가 마음에 드는 상품이 있어서 해당 상품을 클릭했다. 그는 해당 상품의 상세 정보를 확인하고 싶어 한다." | ||
|
|
||
| ### 2.2. 핵심 기능 정의 | ||
| - **F-1:** 상품 상세정보를 조회할 수 있다. | ||
| - **F-2:** 상품의 총 좋아요 수와 나의 좋아요 여부를 조회할 수 있다. | ||
| - **F-3:** 구매할 상품의 수량을 선택할 수 있다. | ||
|
|
||
| ### 2.3. 유스케이스 흐름 | ||
| * **Main Flow (상세 조회):** | ||
| 1. 사용자가 상품 목록에서 특정 상품을 클릭한다. | ||
| 2. 해당 상푸의 상세 정보(이름, 가격, 설명, 총 좋아요 수, 나의 좋아요 여부 등)를 조회하여 반환한다. | ||
| 3. 화면에 상품 상세 정보가 표시되고, 수량은 '1'로 기본 설정된다. | ||
| * **Alternate Flow (수량 변경):** | ||
| 1. 사용자가 '+' 버튼을 눌러 수량을 '3'으로 변경한다. | ||
| 2. 화면에 수량이 '3'으로 갱신된다. | ||
| * **Exception Flow (상품 없음):** | ||
| 1. 사용자가 존재하지 않는 상품 ID의 URL로 직접 접근한다. | ||
| 2. 상품을 찾을 수 없으므로, "상품을 찾을 수 없습니다."라는 오류를 반환한다. | ||
|
|
||
| --- | ||
|
|
||
| ## **3. 브랜드별 상품 조회** | ||
|
|
||
| ### 3.1. 유저 시나리오 | ||
| > "사용자가 마음에 드는 특정 브랜드를 찾고 있다. 그는 해당 브랜드에서 나오는 상품만 모아서 조회하고 싶어 한다." | ||
|
|
||
| ### 3.2. 핵심 기능 정의 | ||
| - **F-1:** 특정 브랜드에 속한 상품만 필터링하여 조회할 수 있다. | ||
| - **F-2:** 상품 목록을 '인기순'으로 정렬할 수 있다. | ||
| - **F-3:** 상품 목록을 '최신순', '가격순' 등 다른 기준으로도 정렬할 수 있다. | ||
| - **F-4:** 상품 목록을 페이지 단위로 나누어 볼 수 있다. | ||
|
|
||
| ### 3.3. 유스케이스 흐름 | ||
| * **Main Flow (브랜드별 조회):** | ||
| 1. 사용자가 'A 브랜드'를 조회회한다. | ||
| 2. 해당 브랜드드 상품 목록을 반환한다. | ||
| 3. 화면에 'A 브랜드'의 상품 목록만 표시된다. | ||
| * **Exception Flow (브랜드 없음):** | ||
| 1. 사용자가 존재하지 않는 브랜드 ID의 URL로 직접 접근한다. | ||
| 2. "브랜드를 찾을 수 없습니다."라는 오류(404 Not Found)를 반환한다. | ||
|
|
||
| --- | ||
|
|
||
| ## **4. 상품 좋아요 (등록/취소)** | ||
|
|
||
| ### 4.1. 유저 시나리오 | ||
| > (등록) "사용자가 마음에 드는 상품을 발견했다. 그는 나중에 마음에 드는 상품을 모아보기 위해 '좋아요'를 누른다." | ||
| > (취소) "사용자가 이전에 '좋아요' 했던 상품이 더 이상 마음에 들지 않아 '좋아요'를 취소한다." | ||
|
|
||
| ### 4.2. 핵심 기능 정의 | ||
| - **F-1:** 상품에 대해 '좋아요'를 등록할 수 있다. | ||
| - **F-2:** 이미 '좋아요'가 등록된 상품의 '좋아요'를 취소할 수 있다. | ||
|
|
||
| ### 4.3. 유스케이스 흐름 | ||
| * **Main Flow (좋아요 등록):** | ||
| 1. 사용자가 좋아요 아이콘을 클릭한다. | ||
| 2. 좋아요 여부를 판단한다. | ||
| 3. 해당 상품이 좋아요가 안되어있으면 좋아요 등록이 된다. | ||
| * **Alternate Flow (좋아요 취소):** | ||
| 1. 사용자가 좋아요 아이콘을 클릭한다. | ||
| 2. 좋아요 여부를 판단한다. | ||
| 3. 해당 상품이 좋아요가 되어있으면 좋아요가 취소된다. | ||
| * **Exception Flow (비회원):** | ||
| 1. 비로그인 사용자가 좋아요 아이콘을 클릭한다. | ||
| 2. "로그인이 필요한 기능입니다."라는 오류 메시지를 반환한다. | ||
|
|
||
| --- | ||
|
|
||
| ## **5. 주문 생성 (결제)** | ||
|
|
||
| ### 5.1. 유저 시나리오 | ||
| > "사용자가 특정 상품을 구매하고 싶어, 해당 상품을 결제한다." | ||
|
|
||
| ### 5.2. 핵심 기능 정의 | ||
| - **F-1:** 사용자가 선택한 상품을 구매할 수 있다. | ||
| - **F-2:** 사용자의 포인트가 충분할 경우 결제할 수 있으며, 결제 시 포인트가 차감된다. | ||
| - **F-3:** 상품의 재고가 충분할 경우 결제할 수 있으며, 결제 시 재고가 차감된다. | ||
| - **F-4:** 결제 성공 시 주문 정보가 외부 시스템으로 전송된다. | ||
|
|
||
| ### 5.3. 유스케이스 흐름 | ||
| * **Main Flow (주문 성공):** | ||
| 1. 로그인한 사용자가 상품과 수량을 선택하고 '결제하기'를 요청한다. | ||
| 2. 상품 재고가 1개 이상인지 확인한다. | ||
| 3. 사용자 포인트가 총 결제 금액 이상인지 확인한다. | ||
| 4. 상품 재고를 1 차감한다. | ||
| 5. 사용자 포인트를 차감한다. | ||
| 6. '주문' 및 '주문 항목'을 저장한다. | ||
| 7. 주문 정보를 외부 시스템으로 전송한다. | ||
| 8. 사용자에게 "주문 완료" 응답을 반환한다. | ||
| * **Alternate Flow (여러 수량 구매):** | ||
| 1. 사용자가 수량을 '3'으로 선택하고 '결제하기'를 요청한다. | ||
| 2. 재고 및 포인트를 수량 기준으로 검증, 차감, 저장한다. | ||
| 3. 주문에 성공한다. | ||
| * **Exception Flow (재고 부족):** | ||
| 1. 시스템이 재고를 확인했으나, 요청 수량보다 재고가 부족하다. | ||
| 2. "재고가 부족하여 주문할 수 없습니다."라는 오류 메시지를 반환한다. | ||
| * **Exception Flow (포인트 부족):** | ||
| 1. 시스템이 사용자의 포인트를 확인했으나, 총 결제 금액보다 포인트가 부족하다. | ||
| 2. "포인트가 부족합니다."라는 오류 메시지를 반환한다. | ||
| * **Exception Flow (비회원):** | ||
| 1. 비로그인 사용자가 '결제하기'를 요청한다. | ||
| 2. "로그인이 필요한 기능입니다."라는 오류 메시지를 반환한다. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
|
|
||
| ### 1. 상품 목록/상세 조회 및 브랜드 조회 | ||
| ```mermaid | ||
| sequenceDiagram | ||
| participant User | ||
| participant ProductController | ||
| participant ProductReader | ||
|
|
||
| %% 상품 목록 조회 %% | ||
| User->>ProductController: GET /api/v1/products?sort=likes_desc | ||
| ProductController->>ProductReader: getProducts(sort) | ||
| ProductReader-->>ProductController: productList | ||
| ProductController-->>User: productList | ||
|
|
||
| %% 상품 상세 조회 %% | ||
| User->>ProductController: GET /api/v1/products/{productId} | ||
| ProductController->>ProductReader: getProduct(productId) | ||
| ProductReader-->>ProductController: product | ||
| ProductController-->>User: product | ||
|
|
||
| participant BrandController | ||
| participant BrandReader | ||
|
|
||
| %% 브랜드 조회 %% | ||
| User->>BrandController: GET /api/v1/brands/{brandId} | ||
| BrandController->>BrandReader: getBrand({brandId}) | ||
| BrandReader-->>BrandController: brand | ||
| BrandController-->>User: brand | ||
| ``` | ||
| ### 2. 주문 생성 | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant User | ||
| participant OrderController | ||
| participant OrderService | ||
| participant ProductReader | ||
| participant PointReader | ||
| participant ProductService | ||
| participant PointService | ||
| participant OrderRepository | ||
|
|
||
| User->>OrderController: POST /api/v1/orders (body: {productId, quantity}) | ||
| OrderController->>OrderService: setOrder(userId, {productId, quantity}) | ||
|
|
||
| %% 조회 및 검증 %% | ||
| OrderService->>ProductReader: getProduct({productId}) | ||
| ProductReader -->>OrderService: product(현재가격, 재고) | ||
|
|
||
| OrderService->>PointReader: getPoint(userId) | ||
| PointReader-->>OrderService: point (현재 잔여 포인트) | ||
|
|
||
| %% 재고 및 포인트 차감 %% | ||
| critical Transaction Block | ||
| OrderService ->>ProductService: decreaseStock(productId, quantity) | ||
| OrderService ->>PointService: deductPoint(productPrice * quantity) | ||
| OrderService->> OrderRepository: save(new Order(...)) | ||
| OrderRepository-->>OrderService: orderInfo | ||
| end | ||
|
|
||
| %% 응답 %% | ||
| OrderService -->> OrderController:orderInfo | ||
| OrderController-->> User:orderInfo | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| ## 1. 클래스 다이어그램램 | ||
|
|
||
| ```mermaid | ||
| classDiagram | ||
| class User { | ||
| Long id | ||
| String name | ||
| int point | ||
| } | ||
| class Product { | ||
| Long id | ||
| String name | ||
| int price | ||
| int quantity | ||
| } | ||
| class Brand { | ||
| Long id | ||
| String name | ||
| } | ||
| class Like { | ||
| User user | ||
| Product product | ||
| } | ||
| class Order { | ||
| Long id | ||
| User user | ||
| int totalPrice | ||
| Timestamp orderDate | ||
| } | ||
| class OrderItem { | ||
| Order order | ||
| Product product | ||
| int quantity | ||
| int orderPrice | ||
| } | ||
|
|
||
| %% --- 관계 정의 --- | ||
| Product --> Brand : (상품은 브랜드를 가짐) | ||
|
|
||
| Order --> User : (주문은 유저를 가짐) | ||
|
|
||
| OrderItem --> Order : (주문 항목은 주문에 속함) | ||
| OrderItem --> Product : (주문 항목은 상품을 가짐) | ||
|
|
||
| Like --> User : (좋아요는 유저를 가짐) | ||
| Like -- > Product : (좋아요는 상품을 가짐) | ||
leeminkyu-kr96 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| ## 1. ERD | ||
|
|
||
| ```mermaid | ||
| erDiagram | ||
| users { | ||
| bigint id PK | ||
| varchar name | ||
| int point | ||
| } | ||
| products { | ||
| bigint id PK | ||
| varchar name | ||
| int price | ||
| int stock_quantity | ||
| bigint brand_id FK | ||
| } | ||
| brands { | ||
| bigint id PK | ||
| varchar name | ||
| } | ||
| likes { | ||
| bigint user_id PK, FK | ||
| bigint product_id PK, FK | ||
| } | ||
| orders { | ||
| bigint id PK | ||
| bigint user_id FK | ||
| int total_price | ||
| Timestamp created_at | ||
| } | ||
| orderitems { | ||
| bigint id PK | ||
| bigint order_id FK | ||
| bigint product_id FK | ||
| int quantity | ||
| int order_price | ||
| } | ||
|
|
||
| %% --- 관계 정의 (1:N) --- | ||
| users ||--o{ likes : "likes" | ||
| users ||--o{ orders : "places" | ||
|
|
||
| products ||--o{ likes : "is_liked" | ||
| products ||--o{ orderitems : "is_in" | ||
|
|
||
| brands ||--o{ products : "has" | ||
|
|
||
| orders ||--o{ orderitems : "contains" | ||
leeminkyu-kr96 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
22 changes: 22 additions & 0 deletions
22
apps/commerce-api/src/main/java/com/loopers/application/brand/BrandFacade.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.loopers.application.brand; | ||
|
|
||
| import com.loopers.domain.product.Brand; | ||
| import com.loopers.support.error.CoreException; | ||
| import com.loopers.support.error.ErrorType; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Component | ||
| public class BrandFacade { | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public BrandInfo getBrand(String brandName) { | ||
| if (brandName == null || brandName.isBlank()) { | ||
| throw new CoreException(ErrorType.BAD_REQUEST, "브랜드 이름은 필수입니다."); | ||
| } | ||
|
|
||
| Brand brand = new Brand(brandName); | ||
| return BrandInfo.from(brand); | ||
| } | ||
leeminkyu-kr96 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
10 changes: 10 additions & 0 deletions
10
apps/commerce-api/src/main/java/com/loopers/application/brand/BrandInfo.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.loopers.application.brand; | ||
|
|
||
| import com.loopers.domain.product.Brand; | ||
|
|
||
| public record BrandInfo(String name) { | ||
| public static BrandInfo from(Brand brand) { | ||
| return new BrandInfo(brand.name()); | ||
| } | ||
| } | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.