Thanks to visit codestin.com
Credit goes to github.com

Skip to content

EarlyExpress/product-service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

56 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Product Service

Early Express ν”Œλž«νΌμ˜ μƒν’ˆ 관리λ₯Ό λ‹΄λ‹Ήν•˜λŠ” λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€

πŸ“‹ κ°œμš”

Product ServiceλŠ” μƒν’ˆμ˜ 등둝, μˆ˜μ •, μ‚­μ œ, μƒνƒœ 관리 및 재고 연동을 λ‹΄λ‹Ήν•©λ‹ˆλ‹€. DDD(Domain-Driven Design) μ•„ν‚€ν…μ²˜λ₯Ό 기반으둜 μ„€κ³„λ˜μ—ˆμœΌλ©°, Inventory Serviceμ™€μ˜ 이벀트 기반 톡신을 톡해 재고 μƒνƒœλ₯Ό λ™κΈ°ν™”ν•©λ‹ˆλ‹€.

πŸ›  기술 μŠ€νƒ

ꡬ뢄 기술
Framework Spring Boot 3.5.7, Spring Cloud 2025.0.0
Language Java 21
Database PostgreSQL + pgvector
ORM Spring Data JPA, QueryDSL 5.1.0
Message Queue Apache Kafka (Spring Cloud Stream)
Service Discovery Netflix Eureka Client
Config Spring Cloud Config
Security Spring Security, OAuth 2.0 (Keycloak)
Service Communication OpenFeign (User Service 연동)
Observability Micrometer, Zipkin, Loki, Prometheus

πŸ— μ•„ν‚€ν…μ²˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Product Service                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Presentation Layer                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ AllProduct      β”‚ β”‚ ProducerProduct β”‚ β”‚ InternalProduct β”‚   β”‚
β”‚  β”‚ Controller      β”‚ β”‚ Controller      β”‚ β”‚ Controller      β”‚   β”‚
β”‚  β”‚ (곡개 쑰회)      β”‚ β”‚ (생산업체 μ „μš©)  β”‚ β”‚ (μ„œλΉ„μŠ€ κ°„ 톡신) β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Application Layer                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  ProductService (λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 쑰율)                        β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Domain Layer                                                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Product (AR)    β”‚  β”‚ Value Objects                       β”‚  β”‚
β”‚  β”‚ - ProductStatus β”‚  β”‚ - Price                             β”‚  β”‚
β”‚  β”‚ - μƒν’ˆ 둜직      β”‚  β”‚                                     β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Infrastructure Layer                                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚ JPA Entity   β”‚  β”‚ Repository   β”‚  β”‚ UserServiceClient  β”‚    β”‚
β”‚  β”‚ ProductEntityβ”‚  β”‚ Impl         β”‚  β”‚ (Feign)            β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“¦ 도메인 λͺ¨λΈ

Product (Aggregate Root)

μƒν’ˆμ˜ 전체 생λͺ…μ£ΌκΈ°λ₯Ό κ΄€λ¦¬ν•˜λŠ” 핡심 도메인 λͺ¨λΈμž…λ‹ˆλ‹€.

Product
β”œβ”€β”€ productId (μ‹λ³„μž)
β”œβ”€β”€ sellerId (판맀자 ID)
β”œβ”€β”€ companyId (업체 ID)
β”œβ”€β”€ name (μƒν’ˆλͺ…)
β”œβ”€β”€ description (μƒν’ˆ μ„€λͺ…)
β”œβ”€β”€ Price (가격 VO)
β”‚   └── amount (κΈˆμ•‘)
β”œβ”€β”€ ProductStatus (μƒνƒœ)
β”œβ”€β”€ isSellable (판맀 κ°€λŠ₯ μ—¬λΆ€)
β”œβ”€β”€ hasEvent (이벀트 적용 μ—¬λΆ€)
β”œβ”€β”€ minOrderQuantity (μ΅œμ†Œ μ£Όλ¬Έ μˆ˜λŸ‰)
β”œβ”€β”€ maxOrderQuantity (μ΅œλŒ€ μ£Όλ¬Έ μˆ˜λŸ‰)
└── Audit Fields
    β”œβ”€β”€ createdAt / createdBy
    β”œβ”€β”€ updatedAt / updatedBy
    └── deletedAt / deletedBy / isDeleted

μƒν’ˆ μƒνƒœ 흐름 (ProductStatus)

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚      DRAFT       β”‚ μž„μ‹œ μ €μž₯
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚ activate()
                             β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”Œβ”€β”€β”€β”€β”€β”‚      ACTIVE      │─────┐
              β”‚     β”‚   (판맀 쀑)       β”‚     β”‚
              β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
              β”‚              β”‚               β”‚
     suspend()β”‚              β”‚               β”‚InventoryLowStockEvent
              β”‚              β”‚               β”‚ (from Inventory Service)
              β–Ό              β”‚               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  SUSPENDED  β”‚          β”‚     β”‚  OUT_OF_STOCK   β”‚
    β”‚ (판맀 쀑지)  │───────────     β”‚    (ν’ˆμ ˆ)        β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚   activate() β”‚              β”‚
              └───────────────              β”‚InventoryRestockedEvent
                             β”‚              β”‚ (from Inventory Service)
                             β–Ό              β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
                    β”‚   DISCONTINUED   β”‚β—€β”€β”€β”€β”˜ (ν’ˆμ ˆ ν•΄μ œ μ‹œ ACTIVE둜)
                    β”‚     (단쒅)        β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”Œ API μ—”λ“œν¬μΈνŠΈ

Public API (λͺ¨λ“  μ‚¬μš©μž)

Method Endpoint μ„€λͺ…
GET /v1/product/web/all/products μƒν’ˆ λͺ©λ‘ 쑰회 (νŽ˜μ΄μ§•)
GET /v1/product/web/all/products/{productId} μƒν’ˆ 상세 쑰회
GET /v1/product/web/all/products/search μƒν’ˆ 검색 (ν‚€μ›Œλ“œ)

Producer API (생산업체 μ „μš©)

Method Endpoint μ„€λͺ…
POST /v1/product/web/producer/products μƒν’ˆ 등둝
PUT /v1/product/web/producer/products/{productId} μƒν’ˆ μˆ˜μ •
DELETE /v1/product/web/producer/products/{productId} μƒν’ˆ μ‚­μ œ (단쒅)
GET /v1/product/web/producer/products λ‚΄ μƒν’ˆ λͺ©λ‘ 쑰회
PUT /v1/product/web/producer/products/{productId}/activate μƒν’ˆ ν™œμ„±ν™”
PUT /v1/product/web/producer/products/{productId}/suspend μƒν’ˆ μΌμ‹œμ€‘μ§€

Internal API (μ„œλΉ„μŠ€ κ°„ 톡신)

Method Endpoint μ„€λͺ…
GET /v1/product/internal/products/{productId}/validate μƒν’ˆ 쑴재 확인
GET /v1/product/internal/products/{productId} μƒν’ˆ 정보 쑰회
POST /v1/product/internal/products/validate-bulk λŒ€λŸ‰ μƒν’ˆ 검증
GET /v1/product/internal/sellers/{sellerId}/products νŒλ§€μžλ³„ μƒν’ˆ λͺ©λ‘

μƒν’ˆ 등둝 μš”μ²­ μ˜ˆμ‹œ

POST /v1/product/web/producer/products
X-User-Id: seller-uuid

{
  "hubId": "hub-uuid",
  "companyId": "company-uuid",
  "name": "프리미엄 λ…ΈνŠΈλΆ",
  "description": "κ³ μ„±λŠ₯ λΉ„μ¦ˆλ‹ˆμŠ€ λ…ΈνŠΈλΆ",
  "price": 1500000,
  "minOrderQuantity": 1,
  "maxOrderQuantity": 10
}

응닡 μ˜ˆμ‹œ

{
  "productId": "product-uuid",
  "sellerId": "seller-uuid",
  "name": "프리미엄 λ…ΈνŠΈλΆ",
  "description": "κ³ μ„±λŠ₯ λΉ„μ¦ˆλ‹ˆμŠ€ λ…ΈνŠΈλΆ",
  "price": 1500000,
  "status": "DRAFT",
  "isSellable": false,
  "hasEvent": false,
  "minOrderQuantity": 1,
  "maxOrderQuantity": 10,
  "createdAt": "2025-01-15T10:30:00",
  "updatedAt": null
}

λŒ€λŸ‰ μƒν’ˆ 검증 μš”μ²­/응닡

POST /v1/product/internal/products/validate-bulk
{
  "productIds": ["product-1", "product-2", "product-3"]
}
{
  "allValid": false,
  "validProductIds": ["product-1", "product-2"],
  "invalidProductIds": ["product-3"],
  "errors": {
    "product-3": "μƒν’ˆμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."
  }
}

βš™οΈ ν™˜κ²½ μ„€μ •

ν•„μˆ˜ ν™˜κ²½ λ³€μˆ˜

# Application
APP_PORT=4012
APP_NAME=product-service
APP_PROFILE=dev

# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=default_db
DB_USERNAME=postgres
DB_PASSWORD=postgres123!

# Eureka
EUREKA_DEFAULT_ZONE=https://www.pinjun.xyz/eureka1/eureka/,https://www.pinjun.xyz/eureka2/eureka/
EUREKA_INSTANCE_HOSTNAME=192.168.0.42

# Config Server
CONFIG_SERVER_URL=https://www.pinjun.xyz/config

# Kafka
KAFKA_BOOTSTRAP_SERVERS=61.254.69.188:9092,61.254.69.188:9093,61.254.69.188:9094
KAFKA_CONSUMER_GROUP_ID=product-service-group

# Keycloak (OAuth 2.0)
KEYCLOAK_ISSUER_URI=https://www.pinjun.xyz/keycloak/realms/codefactory
KEYCLOAK_CLIENT_ID=user
KEYCLOAK_CLIENT_SECRET=user-password

# User Service (Feign)
USER_SERVICE_URL=http://user-service:8081

# Observability
ZIPKIN_ENABLED=true
ZIPKIN_BASE_URL=https://www.pinjun.xyz/zipkin
LOKI_ENABLED=true
LOKI_URL=https://www.pinjun.xyz/loki/api/v1/push
PROMETHEUS_PUSHGATEWAY_ENABLED=true
PROMETHEUS_PUSHGATEWAY_URL=https://www.pinjun.xyz/prometheus/pushgateway

πŸš€ μ‹€ν–‰ 방법

둜컬 개발 ν™˜κ²½

# 1. ν™˜κ²½ λ³€μˆ˜ μ„€μ •
cp .env.example .env
# .env 파일 μˆ˜μ •

# 2. Gradle λΉŒλ“œ
./gradlew clean build

# 3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰
./gradlew bootRun

# λ˜λŠ” JAR 직접 μ‹€ν–‰
java -jar build/libs/product-service-0.0.1-SNAPSHOT.jar

Docker μ‹€ν–‰

docker build -t product-service .
docker run -p 4012:4012 --env-file .env product-service

πŸ“¨ Kafka 이벀트

Product ServiceλŠ” ν† ν”½ 뢄리 νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬ 이벀트λ₯Ό λ°œν–‰/μˆ˜μ‹ ν•©λ‹ˆλ‹€.

이벀트 흐름도

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Product Service β”‚                              β”‚ Inventory Service β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                                                 β”‚
         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
         β”‚  β”‚       Topic: product-created            β”‚   β”‚
         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
         β”‚                      β”‚                         β”‚
         β”‚ ProductCreatedEvent  β”‚                         β”‚
         │──────────────────────┼────────────────────────▢│
         β”‚                      β”‚                         β”‚ 재고 μ΄ˆκΈ°ν™”
         β”‚                      β”‚                         β”‚
         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
         β”‚  β”‚       Topic: product-deleted            β”‚   β”‚
         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
         β”‚                      β”‚                         β”‚
         β”‚ ProductDeletedEvent  β”‚                         β”‚
         │──────────────────────┼────────────────────────▢│
         β”‚                      β”‚                         β”‚ 재고 λΉ„ν™œμ„±ν™”
         β”‚                      β”‚                         β”‚
         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
         β”‚  β”‚       Topic: inventory-low-stock        β”‚   β”‚
         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
         β”‚                      β”‚                         β”‚
         │◀─────────────────────┼─────────────────────────│
         β”‚InventoryLowStockEventβ”‚                         β”‚
         β”‚ (ν’ˆμ ˆ 처리)           β”‚                         β”‚
         β”‚                      β”‚                         β”‚
         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
         β”‚  β”‚       Topic: inventory-restocked        β”‚   β”‚
         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
         β”‚                      β”‚                         β”‚
         │◀─────────────────────┼─────────────────────────│
         β”‚InventoryRestockedEvt β”‚                         β”‚
         β”‚ (ν’ˆμ ˆ ν•΄μ œ)           β”‚                         β”‚

λ°œν–‰ 이벀트 (Publisher)

Topic Event μ„€λͺ… λ°œν–‰ μ‹œμ 
product-created ProductCreatedEvent μƒν’ˆ 생성 μƒν’ˆ 등둝 μ‹œ
product-updated ProductUpdatedEvent μƒν’ˆ μˆ˜μ • μƒν’ˆ 정보 λ³€κ²½ μ‹œ
product-deleted ProductDeletedEvent μƒν’ˆ μ‚­μ œ(단쒅) μƒν’ˆ 단쒅 μ‹œ
product-status-changed ProductStatusChangedEvent μƒνƒœ λ³€κ²½ μƒν’ˆ μƒνƒœ 전이 μ‹œ
// ProductCreatedEvent μ˜ˆμ‹œ
{
  "eventId": "uuid",
  "eventType": "PRODUCT_CREATED",
  "source": "product-service",
  "productId": "product-uuid",
  "sellerId": "seller-uuid",
  "hubId": "hub-uuid",
  "name": "프리미엄 λ…ΈνŠΈλΆ",
  "createdAt": "2025-01-15T10:30:00"
}
// ProductDeletedEvent μ˜ˆμ‹œ
{
  "eventId": "uuid",
  "eventType": "PRODUCT_DELETED",
  "source": "product-service",
  "productId": "product-uuid",
  "sellerId": "seller-uuid",
  "deletedAt": "2025-01-15T15:00:00"
}
// ProductStatusChangedEvent μ˜ˆμ‹œ
{
  "eventId": "uuid",
  "eventType": "PRODUCT_STATUS_CHANGED",
  "source": "product-service",
  "productId": "product-uuid",
  "oldStatus": "DRAFT",
  "newStatus": "ACTIVE",
  "changedAt": "2025-01-15T11:00:00"
}

μˆ˜μ‹  이벀트 (Consumer)

Topic Event μ„€λͺ… 처리
inventory-low-stock InventoryLowStockEvent 재고 λΆ€μ‘± μ•Œλ¦Ό ProductService.markAsOutOfStock()
inventory-restocked InventoryRestockedEvent μž¬μž…κ³  μ•Œλ¦Ό ProductService.restoreFromOutOfStock()
// InventoryLowStockEvent μ˜ˆμ‹œ
{
  "eventId": "uuid",
  "eventType": "INVENTORY_LOW_STOCK",
  "source": "inventory-service",
  "inventoryId": "inventory-uuid",
  "productId": "product-uuid",
  "hubId": "hub-uuid",
  "currentQuantity": 5,
  "safetyStock": 10,
  "detectedAt": "2025-01-15T14:00:00"
}
// InventoryRestockedEvent μ˜ˆμ‹œ
{
  "eventId": "uuid",
  "eventType": "INVENTORY_RESTOCKED",
  "source": "inventory-service",
  "inventoryId": "inventory-uuid",
  "productId": "product-uuid",
  "hubId": "hub-uuid",
  "restockedQuantity": 100,
  "currentQuantity": 105,
  "restockedAt": "2025-01-15T16:00:00"
}

Kafka μ„€μ •

spring:
  kafka:
    topic:
      # λ°œν–‰ ν† ν”½
      product-created: product-created
      product-updated: product-updated
      product-deleted: product-deleted
      product-status-changed: product-status-changed
      # μˆ˜μ‹  ν† ν”½
      inventory-low-stock: inventory-low-stock
      inventory-restocked: inventory-restocked
    consumer:
      group-id: product-service-group
      enable-auto-commit: false  # μˆ˜λ™ ACK

πŸ”— μ„œλΉ„μŠ€ κ°„ 톡신

User Service (Feign Client)

@FeignClient(name = "user-service")
public interface UserServiceClient {
    @GetMapping("/api/v1/users/{userId}")
    UserInfoResponse getUserInfo(@PathVariable String userId);
}
// UserInfoResponse
{
  "userId": "user-uuid",
  "username": "seller01",
  "hubId": "hub-uuid",
  "companyId": "company-uuid",
  "role": "SELLER"
}

πŸ” λ³΄μ•ˆ

  • OAuth 2.0 Resource Server (Keycloak 연동)
  • X-User-Id 헀더λ₯Ό ν†΅ν•œ μ‚¬μš©μž 식별
  • Producer APIλŠ” 판맀자 κΆŒν•œ 검증
  • Internal APIλŠ” μ„œλΉ„μŠ€ κ°„ 톡신 μ „μš© (Gateway λ―Έλ…ΈμΆœ)

πŸ“ˆ λͺ¨λ‹ˆν„°λ§

도ꡬ μš©λ„ μ—”λ“œν¬μΈνŠΈ
Actuator ν—¬μŠ€μ²΄ν¬/λ©”νŠΈλ¦­ /actuator/health, /actuator/prometheus
Zipkin λΆ„μ‚° 좔적 Push to Zipkin Server
Loki 둜그 μˆ˜μ§‘ Push via Logback Appender
Prometheus λ©”νŠΈλ¦­ μˆ˜μ§‘ Push to Pushgateway

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰

src/main/java/com/early_express/product_service/
β”œβ”€β”€ domain/product/
β”‚   β”œβ”€β”€ application/
β”‚   β”‚   └── service/
β”‚   β”‚       └── ProductService.java
β”‚   β”œβ”€β”€ domain/
β”‚   β”‚   β”œβ”€β”€ model/
β”‚   β”‚   β”‚   β”œβ”€β”€ Product.java (Aggregate Root)
β”‚   β”‚   β”‚   └── vo/
β”‚   β”‚   β”‚       β”œβ”€β”€ Price.java
β”‚   β”‚   β”‚       └── ProductStatus.java
β”‚   β”‚   β”œβ”€β”€ exception/
β”‚   β”‚   β”œβ”€β”€ messaging/
β”‚   β”‚   β”‚   β”œβ”€β”€ ProductEventPublisher.java (Interface)
β”‚   β”‚   β”‚   └── dto/
β”‚   β”‚   β”‚       β”œβ”€β”€ ProductCreatedEventData.java
β”‚   β”‚   β”‚       β”œβ”€β”€ ProductUpdatedEventData.java
β”‚   β”‚   β”‚       β”œβ”€β”€ ProductDeletedEventData.java
β”‚   β”‚   β”‚       └── ProductStatusChangedEventData.java
β”‚   β”‚   └── repository/
β”‚   β”œβ”€β”€ infrastructure/
β”‚   β”‚   β”œβ”€β”€ client/user/
β”‚   β”‚   β”‚   β”œβ”€β”€ UserServiceClient.java
β”‚   β”‚   β”‚   └── dto/
β”‚   β”‚   β”‚       └── UserInfoResponse.java
β”‚   β”‚   β”œβ”€β”€ messaging/
β”‚   β”‚   β”‚   β”œβ”€β”€ inventory/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ consumer/
β”‚   β”‚   β”‚   β”‚   β”‚   └── InventoryEventConsumer.java
β”‚   β”‚   β”‚   β”‚   └── event/
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ InventoryLowStockEvent.java
β”‚   β”‚   β”‚   β”‚       └── InventoryRestockedEvent.java
β”‚   β”‚   β”‚   └── product/
β”‚   β”‚   β”‚       β”œβ”€β”€ producer/
β”‚   β”‚   β”‚       β”‚   └── KafkaProductEventPublisher.java
β”‚   β”‚   β”‚       └── event/
β”‚   β”‚   β”‚           β”œβ”€β”€ ProductCreatedEvent.java
β”‚   β”‚   β”‚           β”œβ”€β”€ ProductUpdatedEvent.java
β”‚   β”‚   β”‚           β”œβ”€β”€ ProductDeletedEvent.java
β”‚   β”‚   β”‚           └── ProductStatusChangedEvent.java
β”‚   β”‚   └── persistence/
β”‚   β”‚       └── entity/
β”‚   β”‚           └── ProductEntity.java
β”‚   └── presentation/
β”‚       β”œβ”€β”€ web/
β”‚       β”‚   β”œβ”€β”€ AllProductController.java
β”‚       β”‚   β”œβ”€β”€ ProducerProductController.java
β”‚       β”‚   └── dto/
β”‚       β”‚       β”œβ”€β”€ request/
β”‚       β”‚       β”‚   β”œβ”€β”€ CreateProductRequest.java
β”‚       β”‚       β”‚   └── UpdateProductRequest.java
β”‚       β”‚       └── response/
β”‚       β”‚           └── ProductResponse.java
β”‚       └── internal/
β”‚           β”œβ”€β”€ InternalProductController.java
β”‚           └── dto/
β”‚               β”œβ”€β”€ request/
β”‚               β”‚   └── ValidateProductsRequest.java
β”‚               └── response/
β”‚                   β”œβ”€β”€ InternalProductResponse.java
β”‚                   └── ProductValidationResponse.java
└── global/
    β”œβ”€β”€ common/
    β”œβ”€β”€ config/
    β”œβ”€β”€ infrastructure/
    β”‚   └── event/base/
    β”‚       └── BaseEvent.java
    └── presentation/
        └── dto/
            └── PageResponse.java

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors