Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
555 changes: 555 additions & 0 deletions .docs/design/03-class-diagram.md

Large diffs are not rendered by default.

351 changes: 351 additions & 0 deletions .docs/design/04-erd.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ erDiagram
orders ||--|{ order_items : "์ฃผ๋ฌธ ํ•ญ๋ชฉ"
coupons ||--o{ user_coupons : "์ฟ ํฐ ๋ฐœ๊ธ‰"
users ||--o{ user_coupons : "๋ณด์œ  ์ฟ ํฐ"
products ||--o{ product_metrics : "๋ฉ”ํŠธ๋ฆญ ์ง‘๊ณ„"
products ||--o{ mv_product_rank_weekly : "์ฃผ๊ฐ„ ๋žญํ‚น"
products ||--o{ mv_product_rank_monthly : "์›”๊ฐ„ ๋žญํ‚น"
orders ||--o| payments : "๊ฒฐ์ œ ์ •๋ณด"
event_outbox }o--|| orders : "์ฃผ๋ฌธ ์ด๋ฒคํŠธ"
event_outbox }o--|| payments : "๊ฒฐ์ œ ์ด๋ฒคํŠธ"
event_outbox }o--|| likes : "์ข‹์•„์š” ์ด๋ฒคํŠธ"
event_inbox }o--|| catalog_events : "์นดํƒˆ๋กœ๊ทธ ์ด๋ฒคํŠธ"
event_inbox }o--|| order_events : "์ฃผ๋ฌธ ์ด๋ฒคํŠธ"

users {
bigint id PK
Expand Down Expand Up @@ -119,6 +128,94 @@ erDiagram
timestamp updated_at
timestamp deleted_at
}

product_metrics {
bigint product_id PK
int like_count
int view_count
int order_count
decimal(15_2) sales_amount
int version
timestamp created_at
timestamp updated_at
}

mv_product_rank_weekly {
bigint id PK
bigint product_id
varchar(10) year_week
int rank_position
double total_score
int like_count
int view_count
int order_count
decimal(15_2) sales_amount
timestamp created_at
timestamp updated_at
}

mv_product_rank_monthly {
bigint id PK
bigint product_id
varchar(7) year_month
int rank_position
double total_score
int like_count
int view_count
int order_count
decimal(15_2) sales_amount
timestamp created_at
timestamp updated_at
}

payments {
bigint id PK
varchar(100) transaction_key UK
varchar(20) order_id
varchar(10) user_id
decimal(19_2) amount
varchar(20) status
varchar(500) failure_reason
varchar(50) card_type
varchar(50) card_no
timestamp created_at
timestamp updated_at
}

event_outbox {
bigint id PK
varchar(50) aggregate_type
varchar(100) aggregate_id
varchar(100) event_type
text payload
varchar(20) status
int retry_count
text error_message
timestamp created_at
timestamp updated_at
}

event_inbox {
bigint id PK
varchar(100) event_id UK
varchar(50) aggregate_type
varchar(100) aggregate_id
varchar(100) event_type
text payload
timestamp processed_at
timestamp created_at
}

dead_letter_queue {
bigint id PK
varchar(50) topic
int partition_number
bigint offset_value
varchar(100) event_type
text payload
text error_message
timestamp created_at
}
```

## ๐Ÿ“ฆ ํ…Œ์ด๋ธ”๋ณ„ ์ƒ์„ธ ์„ค๊ณ„
Expand Down Expand Up @@ -457,6 +554,100 @@ INDEX idx_user_id_is_used (user_id, is_used, deleted_at)

---

## 11. product_metrics (์ƒํ’ˆ ๋ฉ”ํŠธ๋ฆญ ์ง‘๊ณ„)

**์„ค๋ช…**: ์‹ค์‹œ๊ฐ„ ์ƒํ’ˆ ์ด๋ฒคํŠธ ์ง‘๊ณ„๋ฅผ ์ €์žฅํ•˜๋Š” ํ…Œ์ด๋ธ” (Round 9 ์ถ”๊ฐ€)

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| product_id | bigint | PK | ์ƒํ’ˆ ID |
| like_count | int | NOT NULL, DEFAULT 0 | ์ข‹์•„์š” ์ˆ˜ |
| view_count | int | NOT NULL, DEFAULT 0 | ์กฐํšŒ ์ˆ˜ |
| order_count | int | NOT NULL, DEFAULT 0 | ์ฃผ๋ฌธ ์ˆ˜ |
| sales_amount | decimal(15,2) | NOT NULL, DEFAULT 0.00 | ํŒ๋งค ๊ธˆ์•ก |
| version | int | NOT NULL, DEFAULT 0 | ๋‚™๊ด€์  ๋ฝ ๋ฒ„์ „ |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ |
| updated_at | timestamp | NOT NULL | ์ˆ˜์ • ์‹œ๊ฐ„ |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- Kafka ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ง‘๊ณ„
- `version` ํ•„๋“œ๋ฅผ ํ†ตํ•œ ๋‚™๊ด€์  ๋ฝ์œผ๋กœ ๋™์‹œ์„ฑ ์ œ์–ด
- ์ผ๋ณ„ ๋ฐ์ดํ„ฐ ์ง‘๊ณ„

**์ธ๋ฑ์Šค**:
```sql
INDEX idx_like_count (like_count DESC)
INDEX idx_view_count (view_count DESC)
INDEX idx_order_count (order_count DESC)
INDEX idx_updated_at (updated_at)
```

---
Comment on lines +557 to +585
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

๐Ÿ Script executed:

# Search for ProductMetricsAggregateReader and related query implementations
rg -i 'ProductMetricsAggregateReader|ProductMetricsReader|product_metrics.*read' --type java -A 15

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 12624


๐Ÿ Script executed:

# Search for usage of like_count, view_count, order_count in queries
rg 'like_count|view_count|order_count' --type java -B 3 -A 3 | head -60

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 6687


๐Ÿ Script executed:

# Search for scoring weight definitions and ranking logic
rg -i 'weight|score.*like|score.*view|score.*order|ranking' --type java | head -30

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 4174


๐Ÿ Script executed:

# Check for sort order definitions related to metrics
rg 'DESC|ORDER BY.*like_count|ORDER BY.*view_count|ORDER BY.*order_count' --type java -B 2 -A 2

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 8385


๐Ÿ Script executed:

# Search for other queries that use product_metrics table directly
rg 'FROM product_metrics|product_metrics.*WHERE' --type java -B 2 -A 8

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 1515


๐Ÿ Script executed:

# Check the full ProductMetricsAggregateReader implementation
sed -n '30,90p' apps/commerce-batch/src/main/java/com/loopers/batch/reader/ProductMetricsAggregateReader.java

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 2093


๐Ÿ Script executed:

# Check RankingScoreProcessor for scoring logic details
rg -A 30 'class RankingScoreProcessor' --type java

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 3914


๐Ÿ Script executed:

# Search for all queries on product_metrics to understand full usage pattern
rg 'product_metrics' --type java -B 3 -A 5

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 5423


๐Ÿ Script executed:

# Check if there are any queries that would use individual metric DESC indexes
rg 'ORDER BY.*like_count|ORDER BY.*view_count|ORDER BY.*order_count' --type java

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 70


product_metrics ์ธ๋ฑ์Šค ์„ค๊ณ„ ์žฌ๊ฒ€ํ†  ํ•„์š”

์ ์ˆ˜ ๊ณ„์‚ฐ ๊ณต์‹๊ณผ ๊ฐ€์ค‘์น˜(view: 0.1, like: 0.2, order: 0.6)๋Š” ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์ ์šฉ๋˜๊ณ  ์žˆ์œผ๋‚˜, ๋ฌธ์„œํ™”๋œ DESC ์ธ๋ฑ์Šค(like_count, view_count, order_count DESC)๋Š” ์‹ค์ œ Top-N ์ฟผ๋ฆฌ์—์„œ ํ™œ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ์ฟผ๋ฆฌ ์‹คํ–‰ ํŒจํ„ด:

  • WHERE created_at >= :startDate AND created_at < :endDate (์ธ๋ฑ์Šค ์—†์Œ)
  • GROUP BY product_id
  • ORDER BY total_score DESC (๊ณ„์‚ฐ๋œ ํ•„๋“œ, ์ธ๋ฑ์Šค ๋ถˆ๊ฐ€)

ํ˜„์žฌ ๊ฐœ๋ณ„ ๋ฉ”ํŠธ๋ฆญ DESC ์ธ๋ฑ์Šค๋Š” ์ด ์ฟผ๋ฆฌ์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฉฐ, created_at ๋ฒ”์œ„ ํ•„ํ„ฐ๋ง๊ณผ ๊ณ„์‚ฐ๋œ total_score ์ •๋ ฌ๋กœ ์ธํ•œ filesort ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ์„ ๊ณ ๋ คํ•˜์—ฌ created_at ์ธ๋ฑ์‹ฑ ๋˜๋Š” ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์ „๋žต์„ ์žฌํ‰๊ฐ€ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.


## 12. mv_product_rank_weekly (์ฃผ๊ฐ„ ๋žญํ‚น)

**์„ค๋ช…**: ์ฃผ๊ฐ„ ์ƒํ’ˆ ๋žญํ‚น Materialized View (Round 10 ์ถ”๊ฐ€)

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| id | bigint | PK, AUTO_INCREMENT | ๋žญํ‚น ๊ณ ์œ  ๋ฒˆํ˜ธ |
| product_id | bigint | NOT NULL | ์ƒํ’ˆ ID |
| year_week | varchar(10) | NOT NULL | ISO Week ํ˜•์‹ (YYYY-Wnn) |
| rank_position | int | NOT NULL | ์ˆœ์œ„ (1~100) |
| total_score | double | NOT NULL | ์ด์  |
| like_count | int | NOT NULL | ์ข‹์•„์š” ์ˆ˜ |
| view_count | int | NOT NULL | ์กฐํšŒ ์ˆ˜ |
| order_count | int | NOT NULL | ์ฃผ๋ฌธ ์ˆ˜ |
| sales_amount | decimal(15,2) | NOT NULL | ํŒ๋งค ๊ธˆ์•ก |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ |
| updated_at | timestamp | NOT NULL | ์ˆ˜์ • ์‹œ๊ฐ„ |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- Spring Batch๋กœ ์ฃผ 1ํšŒ ์ง‘๊ณ„ (๋งค์ฃผ ์›”์š”์ผ 01:00)
- TOP 100๋งŒ ์ €์žฅ
- ์ ์ˆ˜ ๊ณ„์‚ฐ: `(view_count ร— 0.1) + (like_count ร— 0.2) + (order_count ร— 0.6 ร— log10(sales_amount + 1))`

**์ธ๋ฑ์Šค**:
```sql
UNIQUE KEY uk_product_week (product_id, year_week)
INDEX idx_year_week_rank (year_week, rank_position)
INDEX idx_year_week_score (year_week, total_score DESC)
```

---

## 13. mv_product_rank_monthly (์›”๊ฐ„ ๋žญํ‚น)

**์„ค๋ช…**: ์›”๊ฐ„ ์ƒํ’ˆ ๋žญํ‚น Materialized View (Round 10 ์ถ”๊ฐ€)

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| id | bigint | PK, AUTO_INCREMENT | ๋žญํ‚น ๊ณ ์œ  ๋ฒˆํ˜ธ |
| product_id | bigint | NOT NULL | ์ƒํ’ˆ ID |
| year_month | varchar(7) | NOT NULL | ๋…„์›” ํ˜•์‹ (YYYY-MM) |
| rank_position | int | NOT NULL | ์ˆœ์œ„ (1~100) |
| total_score | double | NOT NULL | ์ด์  |
| like_count | int | NOT NULL | ์ข‹์•„์š” ์ˆ˜ |
| view_count | int | NOT NULL | ์กฐํšŒ ์ˆ˜ |
| order_count | int | NOT NULL | ์ฃผ๋ฌธ ์ˆ˜ |
| sales_amount | decimal(15,2) | NOT NULL | ํŒ๋งค ๊ธˆ์•ก |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ |
| updated_at | timestamp | NOT NULL | ์ˆ˜์ • ์‹œ๊ฐ„ |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- Spring Batch๋กœ ์›” 1ํšŒ ์ง‘๊ณ„ (๋งค์›” 1์ผ 02:00)
- TOP 100๋งŒ ์ €์žฅ
- ์ ์ˆ˜ ๊ณ„์‚ฐ: ์ฃผ๊ฐ„ ๋žญํ‚น๊ณผ ๋™์ผ

**์ธ๋ฑ์Šค**:
```sql
UNIQUE KEY uk_product_month (product_id, year_month)
INDEX idx_year_month_rank (year_month, rank_position)
INDEX idx_year_month_score (year_month, total_score DESC)
```

---

## ์—…๋ฐ์ดํŠธ๋œ order_items ํ…Œ์ด๋ธ” (์Šค๋ƒ…์ƒท ํŒจํ„ด)

**๋ณ€๊ฒฝ ์‚ฌํ•ญ**: `product_id`๋ฅผ FK์—์„œ ์ผ๋ฐ˜ ์ปฌ๋Ÿผ์œผ๋กœ ๋ณ€๊ฒฝ, ์Šค๋ƒ…์ƒท ํ•„๋“œ ์ถ”๊ฐ€
Expand All @@ -479,6 +670,166 @@ INDEX idx_user_id_is_used (user_id, is_used, deleted_at)

---

## 14. payments (๊ฒฐ์ œ)

**์„ค๋ช…**: ๊ฒฐ์ œ ์ •๋ณด ๋ฐ ์ƒํƒœ ๊ด€๋ฆฌ

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| id | bigint | PK, AUTO_INCREMENT | ๊ฒฐ์ œ ๊ณ ์œ  ๋ฒˆํ˜ธ |
| transaction_key | varchar(100) | UNIQUE, NOT NULL | PG์‚ฌ ๊ฑฐ๋ž˜ ํ‚ค (์ค‘๋ณต ๋ฐฉ์ง€) |
| order_id | varchar(20) | NOT NULL | ์ฃผ๋ฌธ ID |
| user_id | varchar(10) | NOT NULL | ์‚ฌ์šฉ์ž ID |
| amount | decimal(19,2) | NOT NULL | ๊ฒฐ์ œ ๊ธˆ์•ก |
| status | varchar(20) | NOT NULL | ๊ฒฐ์ œ ์ƒํƒœ (PENDING, SUCCESS, FAILED) |
| failure_reason | varchar(500) | NULL | ์‹คํŒจ ์‚ฌ์œ  (FAILED ์‹œ ํ•„์ˆ˜) |
| card_type | varchar(50) | NULL | ์นด๋“œ ํƒ€์ž… (CREDIT, DEBIT) |
| card_no | varchar(50) | NULL | ์นด๋“œ ๋ฒˆํ˜ธ (๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ) |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ |
| updated_at | timestamp | NOT NULL | ์ˆ˜์ • ์‹œ๊ฐ„ |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- `transaction_key`๋Š” PG์‚ฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ณ ์œ  ๊ฑฐ๋ž˜ ์‹๋ณ„์ž
- ๊ฒฐ์ œ ์ƒํƒœ๋Š” PENDING โ†’ SUCCESS ๋˜๋Š” FAILED๋กœ๋งŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
- ๊ฒฐ์ œ ์‹คํŒจ ์‹œ `failure_reason` ํ•„์ˆ˜
- `card_no`๋Š” ๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ (์˜ˆ: 1234-****-****-5678)

**์ธ๋ฑ์Šค**:
```sql
UNIQUE INDEX uk_transaction_key (transaction_key)
INDEX idx_order_id (order_id)
INDEX idx_user_id (user_id)
INDEX idx_status (status)
```

---

## 15. event_outbox (์ด๋ฒคํŠธ ์•„์›ƒ๋ฐ•์Šค)

**์„ค๋ช…**: Transactional Outbox ํŒจํ„ด ๊ตฌํ˜„ (Round 8 ์ถ”๊ฐ€)

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| id | bigint | PK, AUTO_INCREMENT | ์•„์›ƒ๋ฐ•์Šค ๊ณ ์œ  ๋ฒˆํ˜ธ |
| aggregate_type | varchar(50) | NOT NULL | ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ ํƒ€์ž… (ORDER, PAYMENT, LIKE) |
| aggregate_id | varchar(100) | NOT NULL | ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ ID |
| event_type | varchar(100) | NOT NULL | ์ด๋ฒคํŠธ ํƒ€์ž… (OrderCreatedEvent ๋“ฑ) |
| payload | text | NOT NULL | ์ด๋ฒคํŠธ ํŽ˜์ด๋กœ๋“œ (JSON) |
| status | varchar(20) | NOT NULL, DEFAULT 'PENDING' | ๋ฐœํ–‰ ์ƒํƒœ (PENDING, PUBLISHED, FAILED) |
| retry_count | int | NOT NULL, DEFAULT 0 | ์žฌ์‹œ๋„ ํšŸ์ˆ˜ |
| error_message | text | NULL | ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ (์‹คํŒจ ์‹œ) |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ |
| updated_at | timestamp | NOT NULL | ์ˆ˜์ • ์‹œ๊ฐ„ |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- ๋น„์ฆˆ๋‹ˆ์Šค ํŠธ๋žœ์žญ์…˜๊ณผ ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ์ด๋ฒคํŠธ ์ €์žฅ
- ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋Š” 3ํšŒ
- 3ํšŒ ์‹คํŒจ ์‹œ status = FAILED (์ˆ˜๋™ ์ฒ˜๋ฆฌ ํ•„์š”)
- OutboxEventPublisher ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ PENDING ์ด๋ฒคํŠธ ๋ฐœํ–‰

**์ธ๋ฑ์Šค**:
```sql
INDEX idx_status_created (status, created_at)
INDEX idx_aggregate (aggregate_type, aggregate_id)
```

**Transactional Outbox ํŒจํ„ด**:
```
[๋ชฉ์ ]
์ด๋ฒคํŠธ ๋ฐœํ–‰ ์‹คํŒจ ์‹œ์—๋„ ์ด๋ฒคํŠธ ์†์‹ค ๋ฐฉ์ง€

[ํ๋ฆ„]
1. ์ฃผ๋ฌธ ์ƒ์„ฑ + EventOutbox ์ €์žฅ (๊ฐ™์€ ํŠธ๋žœ์žญ์…˜)
2. ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹
3. OutboxEventPublisher๊ฐ€ PENDING ์ด๋ฒคํŠธ ์กฐํšŒ
4. Kafka๋กœ ๋ฐœํ–‰
5. ์„ฑ๊ณต โ†’ PUBLISHED / ์‹คํŒจ โ†’ ์žฌ์‹œ๋„
```

---

## 16. event_inbox (์ด๋ฒคํŠธ ์ธ๋ฐ•์Šค)

**์„ค๋ช…**: Event Inbox ํŒจํ„ด ๊ตฌํ˜„ - ๋ฉฑ๋“ฑ์„ฑ ๋ณด์žฅ (Round 9 ์ถ”๊ฐ€)

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| id | bigint | PK, AUTO_INCREMENT | ์ธ๋ฐ•์Šค ๊ณ ์œ  ๋ฒˆํ˜ธ |
| event_id | varchar(100) | UNIQUE, NOT NULL | ์ด๋ฒคํŠธ ๊ณ ์œ  ID (์ค‘๋ณต ๋ฐฉ์ง€) |
| aggregate_type | varchar(50) | NOT NULL | ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ ํƒ€์ž… |
| aggregate_id | varchar(100) | NOT NULL | ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ ID |
| event_type | varchar(100) | NOT NULL | ์ด๋ฒคํŠธ ํƒ€์ž… |
| payload | text | NOT NULL | ์ด๋ฒคํŠธ ํŽ˜์ด๋กœ๋“œ (JSON) |
| processed_at | timestamp | NOT NULL | ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ (์ˆ˜์‹  ์‹œ๊ฐ„) |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- Kafka ์ด๋ฒคํŠธ ์ˆ˜์‹  ์‹œ ์ค‘๋ณต ์ฒ˜๋ฆฌ ๋ฐฉ์ง€
- `event_id`๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด ์ด๋ฒคํŠธ ์Šคํ‚ต (๋ฉฑ๋“ฑ์„ฑ ๋ณด์žฅ)
- ์ฒ˜๋ฆฌ ์„ฑ๊ณต ์‹œ์—๋งŒ ์ €์žฅ

**์ธ๋ฑ์Šค**:
```sql
UNIQUE INDEX uk_event_id (event_id)
INDEX idx_aggregate (aggregate_type, aggregate_id)
INDEX idx_processed_at (processed_at)
```

**Event Inbox ํŒจํ„ด**:
```
[๋ชฉ์ ]
์ค‘๋ณต ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์ง€ (Exactly-once ๋ณด์žฅ)

[ํ๋ฆ„]
1. Kafka ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ 
2. event_id ์ถ”์ถœ
3. event_inbox ํ…Œ์ด๋ธ” ์กฐํšŒ
4. ์ค‘๋ณต์ด๋ฉด โ†’ ์Šคํ‚ต
5. ์ค‘๋ณต ์•„๋‹ˆ๋ฉด โ†’ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰ + event_inbox ์ €์žฅ
```

---

## 17. dead_letter_queue (์‹คํŒจ ๋ฉ”์‹œ์ง€ ์ €์žฅ)

**์„ค๋ช…**: ์ฒ˜๋ฆฌ ์‹คํŒจํ•œ Kafka ๋ฉ”์‹œ์ง€ ์ €์žฅ (Round 9 ์ถ”๊ฐ€)

| ์ปฌ๋Ÿผ๋ช… | ํƒ€์ž… | ์ œ์•ฝ์กฐ๊ฑด | ์„ค๋ช… |
|---|---|---|---|
| id | bigint | PK, AUTO_INCREMENT | DLQ ๊ณ ์œ  ๋ฒˆํ˜ธ |
| topic | varchar(50) | NOT NULL | Kafka ํ† ํ”ฝ๋ช… |
| partition_number | int | NOT NULL | ํŒŒํ‹ฐ์…˜ ๋ฒˆํ˜ธ |
| offset_value | bigint | NOT NULL | ์˜คํ”„์…‹ ๊ฐ’ |
| event_type | varchar(100) | NULL | ์ด๋ฒคํŠธ ํƒ€์ž… |
| payload | text | NOT NULL | ๋ฉ”์‹œ์ง€ ํŽ˜์ด๋กœ๋“œ |
| error_message | text | NOT NULL | ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ |
| created_at | timestamp | NOT NULL | ์ƒ์„ฑ ์‹œ๊ฐ„ (์‹คํŒจ ์‹œ๊ฐ„) |

**๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™**:
- Kafka Consumer์—์„œ ์ฒ˜๋ฆฌ ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€ ์ €์žฅ
- ์ˆ˜๋™์œผ๋กœ ์žฌ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
- ์—๋Ÿฌ ๋ถ„์„ ๋ฐ ๋””๋ฒ„๊น… ์šฉ๋„

**์ธ๋ฑ์Šค**:
```sql
INDEX idx_topic_created (topic, created_at DESC)
INDEX idx_event_type (event_type)
```

**DLQ ํŒจํ„ด**:
```
[๋ชฉ์ ]
์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณ„๋„ ์ €์žฅ์†Œ์— ๋ณด๊ด€ํ•˜์—ฌ ์žฌ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

[ํ๋ฆ„]
1. Kafka ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ˆ์™ธ ๋ฐœ์ƒ
2. dead_letter_queue์— ์ €์žฅ
3. ๋กœ๊ทธ๋กœ ์•Œ๋ฆผ
4. ์ˆ˜๋™์œผ๋กœ ์›์ธ ํŒŒ์•… ํ›„ ์žฌ์ฒ˜๋ฆฌ
```

---

## ๋™์‹œ์„ฑ ์ œ์–ด ์ „๋žต ์š”์•ฝ

### Version ํ•„๋“œ (@Version)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ docker-compose -f ./docker/monitoring-compose.yml up
Root
โ”œโ”€โ”€ apps ( spring-applications )
โ”‚ โ”œโ”€โ”€ ๐Ÿ“ฆ commerce-api
โ”‚ โ”œโ”€โ”€ ๐Ÿ“ฆ commerce-batch
โ”‚ โ””โ”€โ”€ ๐Ÿ“ฆ commerce-streamer
โ”œโ”€โ”€ modules ( reusable-configurations )
โ”‚ โ”œโ”€โ”€ ๐Ÿ“ฆ jpa
Expand Down
Loading