Conversation
- CLAUDE.md 추가 (프로젝트 컨텍스트 및 개발 규칙) - spring-security-crypto 의존성 추가 - ErrorType에 UNAUTHORIZED, USER_NOT_FOUND, PASSWORD_MISMATCH 추가 - MySqlTestContainersConfig에 MYSQL_ROOT_PASSWORD 환경변수 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- User 엔티티 (필드 검증, BCrypt 암호화, 이름 마스킹) - UserRepository 인터페이스 - UserService (회원가입, 조회, 인증, 비밀번호 변경) - UserTest 단위 테스트 47건 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- UserJpaRepository (Spring Data JPA) - UserRepositoryImpl (Repository 구현체) - UserServiceIntegrationTest 통합 테스트 9건 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- UserFacade, UserInfo (Application 계층) - AuthenticatedUser, AuthenticatedUserArgumentResolver (헤더 인증) - WebMvcConfig (ArgumentResolver 등록) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- UserV1Controller (POST /users, GET /users/me, PATCH /users/me/password) - UserV1Dto (요청/응답 DTO) - UserV1ApiSpec (OpenAPI 스펙) - UserV1ApiE2ETest E2E 테스트 12건 - user-v1.http (IntelliJ HTTP Client) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- .claude/commands/create-pr.md (PR 템플릿 기반 자동 생성 스킬) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 01-requirements.md: 도메인별 필드/비즈니스 규칙, 유저 시나리오 - 02-sequence-diagrams.md: 주문/좋아요/브랜드 삭제 시퀀스 다이어그램 - 03-class-diagram.md: 계층별 클래스 구조 다이어그램 - 04-erd.md: 테이블 스키마, 인덱스, FK 정책 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
feat: User 도메인 구현 (회원가입, 내 정보 조회, 비밀번호 변경)
This reverts commit 44bfc36.
- Brand/Product/ProductLike/Order/OrderItem 도메인 필드 정의 - 비즈니스 규칙 (BR-*) 및 검증 규칙 정의 - 유저 시나리오 9개 (US-001~009), 어드민 시나리오 7개 (AS-001~007) - API 명세 및 에러 타입 정의 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 주문 생성 시퀀스 (정상/재고 부족 플로우) - 좋아요 등록 시퀀스 (토글 방식: 신규/취소) - 브랜드 삭제 시퀀스 (Cascade 삭제) - 상품 목록 조회 시퀀스 (좋아요 수 포함) - 어드민 인증 플로우 (Interceptor + ArgumentResolver) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 전체 계층 구조 개요 (Layered Architecture) - Brand/Product/ProductLike/Order 도메인 클래스 - 인증 관련 클래스 (AdminAuthInterceptor, AdminUserArgumentResolver) - 공통 클래스 (BaseEntity, ApiResponse, CoreException, ErrorType) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 6개 테이블 스키마 (users, brands, products, product_likes, orders, order_items) - 인덱스 설계 및 FK 삭제 정책 - 쿼리 최적화 가이드 (좋아요순 정렬, 비관적 락) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- requirements-analysis 스킬 정의 - 요구사항 분석 워크플로우 가이드라인 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Money: 금액 VO, 0 이상 검증, add/multiply 연산 - Stock: 재고 VO, 차감/증가 시 불변식 검증 - Quantity: 수량 VO, 1 이상 검증 - ProductSort: 상품 정렬 Enum (LATEST, PRICE_ASC, LIKES_DESC) - 각 VO에 대한 단위 테스트 포함 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Domain Layer (순수 Java): - Brand: 브랜드 엔티티, create/reconstitute 정적 팩토리 - BrandRepository: Repository 인터페이스 - BrandDomainService: 중복 이름 검증, CRUD 정책 - BrandValidator: 브랜드 존재 검증 Infrastructure Layer: - BrandJpaEntity: JPA 엔티티 (@entity) - BrandMapper: Domain ↔ JPA 변환 - BrandRepositoryImpl: Repository Adapter Test: - FakeBrandRepository: Map 기반 in-memory 구현 - BrandTest, BrandInfoTest: 도메인 단위 테스트 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Domain Layer (순수 Java): - Product: 상품 엔티티, Money/Stock VO 사용 - ProductRepository: Repository 인터페이스 (비관적 락 포함) - ProductDomainService: 재고 차감, CRUD 정책 - ProductValidator: 브랜드 존재 검증 Infrastructure Layer: - ProductJpaEntity: JPA 엔티티 - ProductMapper: Domain ↔ JPA 변환 - ProductRepositoryImpl: Repository Adapter Test: - FakeProductRepository: Map 기반 in-memory 구현 - ProductTest: 재고 차감, soft delete 테스트 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Domain Layer (순수 Java): - Like: 좋아요 엔티티 (userId + productId) - LikeId: 복합키 VO - LikeRepository: Repository 인터페이스 - LikeDomainService: 중복 좋아요 방지, 멱등 취소 정책 Infrastructure Layer: - LikeJpaEntity: JPA 엔티티 (unique constraint) - LikeMapper: Domain ↔ JPA 변환 - LikeRepositoryImpl: Repository Adapter Test: - FakeLikeRepository: Map 기반 in-memory 구현 - LikeTest, LikeDomainServiceTest: 도메인 단위 테스트 정책: - 중복 좋아요 시 CONFLICT 예외 - 좋아요 취소는 멱등 (없어도 예외 없음) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Domain Layer (순수 Java): - Order: 주문 Aggregate Root, 총액 자동 계산 - OrderItem: 주문 항목, 가격 스냅샷 보관 - OrderStatus: 주문 상태 Enum - OrderRepository: Repository 인터페이스 Infrastructure Layer: - OrderJpaEntity: JPA 엔티티 (CascadeType.ALL) - OrderItemJpaEntity: JPA 엔티티 - OrderMapper: Domain ↔ JPA 변환 - OrderRepositoryImpl: Repository Adapter Test: - FakeOrderRepository: Map 기반 in-memory 구현 - OrderTest, OrderItemTest: 도메인 단위 테스트 불변식: - 주문 항목은 1개 이상 - 총액은 OrderItem 합산으로 계산 - OrderItem은 불변 리스트로 보호 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Brand: - BrandService: 브랜드 CRUD 유스케이스 - BrandResult: 응답 DTO Product: - ProductService: 상품 CRUD, 목록 조회 - ProductResult: 응답 DTO Like: - LikeApplicationService: 상품 검증 + 좋아요/취소 - LikeResult: 응답 DTO Order: - OrderApplicationService: 재고 차감 + 주문 생성 - OrderResult, OrderItemResult: 응답 DTO - OrderItemRequest: 요청 DTO Test: - BrandServiceIntegrationTest: 통합 테스트 - LikeApplicationServiceTest: Fake 기반 단위 테스트 - OrderApplicationServiceTest: Fake 기반 단위 테스트 트랜잭션: - 주문 시 비관적 락으로 재고 차감 - @transactional 경계 관리 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Brand API: - BrandV1Controller: 브랜드 목록/상세 조회 - BrandAdminV1Controller: 브랜드 CRUD (관리자) - BrandV1Dto: 요청/응답 DTO - BrandV1ApiSpec, BrandAdminV1ApiSpec: OpenAPI 문서화 Product API: - ProductV1Controller: 상품 목록/상세 조회 - ProductAdminV1Controller: 상품 CRUD (관리자) - ProductV1Dto: 요청/응답 DTO - ProductV1ApiSpec, ProductAdminV1ApiSpec: OpenAPI 문서화 HTTP 테스트 파일: - brand-api.http, brand-admin-api.http - product-api.http, product-admin-api.http Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- domain-implements-task.md: 순수 DDD 구현 계획서 - Domain Layer 순수 Java 원칙 - 애그리게잇 설계 (Order, Product, Brand, Like) - Value Object 설계 (Money, Stock, Quantity) - Repository Interface/Impl 분리 - Fake Repository 테스트 전략 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Brand: - BRAND_NOT_FOUND: 브랜드 미존재 - BRAND_ALREADY_EXISTS: 브랜드명 중복 - BRAND_DELETED: 삭제된 브랜드 Product: - PRODUCT_NOT_FOUND: 상품 미존재 - PRODUCT_DELETED: 삭제된 상품 - INSUFFICIENT_STOCK: 재고 부족 Order: - ORDER_NOT_FOUND: 주문 미존재 - ORDER_ACCESS_DENIED: 주문 접근 권한 없음 Admin: - ADMIN_UNAUTHORIZED: 관리자 권한 필요 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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. Comment |
📌 Summary
🧭 Context & Decision
문제 정의
@Entity,@Table등 JPA 어노테이션을 직접 사용하여 Infrastructure 계층에 결합됨jakarta.persistence.*,org.springframework.*import 금지, Fake Repository로 단위 테스트 가능선택지와 결정
1) 도메인 모델을 어떻게 영속화할 것인가 (Domain 순수성)
고려한 대안
A: Domain Entity에 JPA 어노테이션 직접 사용
B: Domain Entity와 JPA Entity 분리 + Mapper(현재 선택한 방향)
C: Domain Entity 분리 + “도메인 친화” ORM/모듈(예: Spring Data/JPA 스펙 노출 최소화)
-“순수성”은 일부 희생하지만 구현/운영 난이도는 낮출 수 있음 (다만 성공기준의 import 금지와 충돌 가능)
최종 결정
트레이드 오프
2) “여러 Aggregate 협력”은 어디에서 조정할 것인가 (Application vs Domain Service)
고려한 대안
A: 모든 규칙을 각 Aggregate(Entity) 내부에 넣기
B: 협력/트랜잭션은 Application Service, “정책(규칙) + Repository 필요”는 Domain Service
C: UseCase(인터랙터) 패턴으로 더 세분화
최종결정
트레이드오프
3) 재고 동시성(오버셀링)을 어떤 전략으로 막을 것인가
고려한 대안
A: 비관적 락(Pessimistic Lock)
B: 낙관적 락(Optimistic Lock) + 재시도
C: 원자적 업데이트(Atomic Update)
최종결정
트레이드오프
🏗️ Design Overview
변경 범위
domain/common/: Money, Quantity Value Objectsdomain/brand/: Brand Entity, Repository, DomainServicedomain/product/: Product Entity, Stock VO, Repository, DomainServicedomain/like/: Like Entity, LikeId VO, Repository, DomainServicedomain/order/: Order Aggregate Root, OrderItem, Repositoryinfrastructure/persistence/jpa/*/: JPA Entities, Mappers, Repository 구현체application/*/: Application Services, Result DTOsinterfaces/api/*/: REST API Controllers, DTOstest/fake/: Fake Repositoriestest/domain/*/: 도메인 단위 테스트주요 컴포넌트 책임
Money,Stock: 불변 Value Object, 생성 시점 검증, 연산 시 새 인스턴스 반환Brand,Product,Like,Order: 순수 Java 도메인 엔티티, 정적 팩토리 메서드 (create/reconstitute)*DomainService: 단일 BC 내 비즈니스 규칙 캡슐화 (중복 좋아요 방지, 멱등 취소 등)*ApplicationService: 트랜잭션 경계, 여러 BC 조합, 유스케이스 오케스트레이션*Mapper: Domain ↔ JPA Entity 변환Fake*Repository: 테스트용 Map 기반 in-memory 구현🔁 Flow Diagram
주문 생성 Flow
sequenceDiagram participant Client participant OrderApplicationService participant ProductRepository participant Product participant OrderRepository participant Order Client->>OrderApplicationService: placeOrder(userId, items) loop 각 주문 항목 OrderApplicationService->>ProductRepository: findByIdWithLock(productId) ProductRepository-->>OrderApplicationService: Product OrderApplicationService->>Product: decreaseStock(quantity) alt 재고 부족 Product-->>OrderApplicationService: INSUFFICIENT_STOCK 예외 OrderApplicationService-->>Client: 에러 응답 else 재고 충분 Product-->>OrderApplicationService: 성공 OrderApplicationService->>ProductRepository: save(product) end end OrderApplicationService->>Order: create(userId, orderItems) Order-->>OrderApplicationService: Order (총액 자동 계산) OrderApplicationService->>OrderRepository: save(order) OrderRepository-->>OrderApplicationService: saved Order OrderApplicationService-->>Client: OrderResult좋아요 등록 Flow
sequenceDiagram participant Client participant LikeApplicationService participant ProductRepository participant LikeDomainService participant LikeRepository Client->>LikeApplicationService: like(userId, productId) LikeApplicationService->>ProductRepository: findByIdActive(productId) alt 상품 미존재 ProductRepository-->>LikeApplicationService: empty LikeApplicationService-->>Client: NOT_FOUND 예외 else 상품 존재 ProductRepository-->>LikeApplicationService: Product LikeApplicationService->>LikeDomainService: like(userId, productId) LikeDomainService->>LikeRepository: exists(userId, productId) alt 이미 좋아요함 LikeRepository-->>LikeDomainService: true LikeDomainService-->>LikeApplicationService: CONFLICT 예외 LikeApplicationService-->>Client: 에러 응답 else 처음 좋아요 LikeRepository-->>LikeDomainService: false LikeDomainService->>LikeRepository: save(Like.create()) LikeDomainService-->>LikeApplicationService: Like LikeApplicationService-->>Client: LikeResult end end상품 CRUD Flow
sequenceDiagram participant Client participant ProductAdminV1Controller participant ProductService participant ProductDomainService participant ProductValidator participant BrandRepository participant ProductRepository Client->>ProductAdminV1Controller: POST /api/admin/v1/products ProductAdminV1Controller->>ProductService: create(ProductInfo) ProductService->>ProductDomainService: create(info) ProductDomainService->>ProductValidator: validateBrandExists(brandId) ProductValidator->>BrandRepository: findByIdActive(brandId) alt 브랜드 미존재 BrandRepository-->>ProductValidator: empty ProductValidator-->>ProductDomainService: BRAND_NOT_FOUND 예외 ProductDomainService-->>ProductService: 예외 전파 ProductService-->>ProductAdminV1Controller: 예외 전파 ProductAdminV1Controller-->>Client: 에러 응답 else 브랜드 존재 BrandRepository-->>ProductValidator: Brand ProductDomainService->>ProductRepository: save(Product.create()) ProductRepository-->>ProductDomainService: saved Product ProductDomainService-->>ProductService: Product ProductService-->>ProductAdminV1Controller: ProductResult ProductAdminV1Controller-->>Client: 201 Created end