Skip to content

rivkode/market

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

109 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Market API

์‚ฌ์šฉ์ž๊ฐ„ ๊ฑฐ๋ž˜๊ฐ€ ๊ฐ€๋Šฅํ•œ Market API๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ถฐ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค

Test ์ฝ”๋“œ ์ž‘์„ฑ์„ ํ†ตํ•ด ๊ฐ€๋…์„ฑ๊ณผ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.


DB ์„ฑ๋Šฅ ๊ฐœ์„ 

์‹คํ—˜ ๋ฐฉ๋ฒ• ๋ฐ ์žฌํ˜„ ๊ฐ€์ด๋“œ

์ •ํ™•ํ•œ ์„ฑ๋Šฅ ๋น„๊ต๋ฅผ ์œ„ํ•ด, ๋™์ผ ๋ฐ์ดํ„ฐ์…‹๊ณผ ๋™์ผ ์ฟผ๋ฆฌ ์กฐ๊ฑด์—์„œ ์ธ๋ฑ์Šค ์ƒ์„ฑ/์‚ญ์ œ ์ „ํ›„๋ฅผ ๋ฐ˜๋ณต ์ธก์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋”๋ฏธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ SQL: perf/mysql/01_generate_dummy_data.sql
    • product 100,000๊ฑด ์ด์ƒ, orders 100,000๊ฑด ์ด์ƒ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
  • ์ธ๋ฑ์Šค ๊ด€๋ฆฌ SQL: perf/mysql/02_index_manage.sql
    • ์‹คํ—˜ ๋Œ€์ƒ ์ธ๋ฑ์Šค ์ƒ์„ฑ/์‚ญ์ œ ์Šคํฌ๋ฆฝํŠธ ์ œ๊ณต
  • ๋ฒค์น˜๋งˆํฌ ์ฟผ๋ฆฌ SQL: perf/mysql/03_benchmark_queries.sql
    • EXPLAIN ANALYZE ๊ธฐ๋ฐ˜ ์กฐํšŒ ์„ฑ๋Šฅ ๋น„๊ต ์ฟผ๋ฆฌ ์ œ๊ณต
  • ์„ฑ๋Šฅ ์ธก์ • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ: src/test/java/sample/market/performance/IndexPerformanceReportTest.java
    • ์ธ๋ฑ์Šค ์ „/ํ›„ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ณ  ์š”์•ฝํ‘œ ํ˜•ํƒœ๋กœ ์ถœ๋ ฅ
    • ์ˆ˜๋™ ์‹คํ–‰ ์ „์šฉ: -DrunPerfTest=true ์˜ต์…˜ ํ•„์š”

์‹คํ–‰ ์ˆœ์„œ:

  1. docker compose ๋กœ MySQL ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ํ›„ ๋”๋ฏธ ๋ฐ์ดํ„ฐ ์ ์žฌ
  2. ์ธ๋ฑ์Šค ์—†๋Š” ์ƒํƒœ/์žˆ๋Š” ์ƒํƒœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ EXPLAIN ANALYZE ๋น„๊ต
  3. ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰์œผ๋กœ ์ˆ˜์น˜ ์š”์•ฝ
  4. ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์™€ ์ƒ์„ธ ๋ถ„์„์œผ๋กœ ๋ฌธ์„œํ™”

ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์˜ˆ์‹œ:

./gradlew test --tests "sample.market.performance.IndexPerformanceReportTest" -DrunPerfTest=true

์„ฑ๋Šฅ ๊ฐœ์„  ์š”์•ฝ

๊ธฐ๋Šฅ ์ธ๋ฑ์Šค ์ „ ์ฟผ๋ฆฌ ์‹œ๊ฐ„ ์ธ๋ฑ์Šค ํ›„ ์ฟผ๋ฆฌ ์‹œ๊ฐ„ ๊ฐœ์„ ์œจ ๋น„๊ณ 
์ฃผ๋ฌธ ์กฐํšŒ (orders), ์ธ๋ฑ์Šค ์—†์Œ -> ๋‹จ์ผ ์ธ๋ฑ์Šค 47.2ms 1.13ms ์•ฝ 97.6% ๊ฐœ์„  ๋‹จ์ผ ์ธ๋ฑ์Šค (buyer_id), product_id๋Š” ํ›„์† ํ•„ํ„ฐ
์ฃผ๋ฌธ ์กฐํšŒ (orders), ์ธ๋ฑ์Šค ์—†์Œ -> ๋ณตํ•ฉ ์ธ๋ฑ์Šค 47.2ms 0.529ms ์•ฝ 98.9% ๊ฐœ์„  ๋ณตํ•ฉ ์ธ๋ฑ์Šค (buyer_id, product_id), ์กฐ๊ฑด ๋งค์นญ ์ตœ์ 
์ฃผ๋ฌธ ์กฐํšŒ (orders), ๋‹จ์ผ ์ธ๋ฑ์Šค -> ๋ณตํ•ฉ ์ธ๋ฑ์Šค 1.13ms 0.529ms ์•ฝ 53.2% ๊ฐœ์„  ๋‹จ์ผ ์ธ๋ฑ์Šค ๋Œ€๋น„ ์ถ”๊ฐ€ ๊ฐœ์„ 
์ƒํ’ˆ ์กฐํšŒ (product.status) ์ธ๋ฑ์Šค ์ ์šฉ 20.1ms 20.3ms ์•ฝ 1.0% ์„ฑ๋Šฅ ์ €ํ•˜ ๋‹จ์ผ ์ธ๋ฑ์Šค (status, ์นด๋””๋„๋ฆฌํ‹ฐ๊ฐ€ ๋‚ฎ์Œ, ์œ ์˜๋ฏธ ๊ฐœ์„  ์—†์Œ)
์ƒํ’ˆ ์กฐํšŒ + OFFSET ํŽ˜์ด์ง• - 10.2ms - OFFSET 20000
์ƒํ’ˆ ์กฐํšŒ + ์ปค์„œ ํŽ˜์ด์ง• - 0.389ms - id < 20000 offset ๋Œ€๋น„ ์•ฝ 96.2% ๊ฐœ์„ 

์ƒ์„ธ ๋ถ„์„

1) ์ฃผ๋ฌธ ์กฐํšŒ ์ธ๋ฑ์Šค ๋น„๊ต (buyer_id, product_id)

์กฐํšŒ ํŒจํ„ด:

  • WHERE buyer_id = ? AND product_id = ? ORDER BY id DESC LIMIT 100

1-1. ์ธ๋ฑ์Šค ์ „(์‹ค์งˆ์ ์œผ๋กœ PK ์—ญ์ˆœ ์ „์ฒด ์Šค์บ”)

EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE buyer_id = 123
  AND product_id = 123
ORDER BY id DESC
LIMIT 100;
actual time=47.2..47.2
Index scan on orders using PRIMARY (reverse) ... rows=100000
  • buyer_id, product_id๋ฅผ ๋ฐ”๋กœ ํƒ€์ง€ ๋ชปํ•ด full table scan ํ›„ ํ•„ํ„ฐ๋ง์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

1-2. ๋‹จ์ผ ์ธ๋ฑ์Šค (buyer_id_idx) ์‚ฌ์šฉ

EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE buyer_id = 123
  AND product_id = 123
ORDER BY id DESC
LIMIT 100;
actual time=1.05..1.13
Index lookup on orders using buyer_id_idx (buyer_id=123)
  • buyer_id ์กฐ๊ฑด์œผ๋กœ ํ›„๋ณด๊ตฐ์„ ๋น ๋ฅด๊ฒŒ ์ค„์—ฌ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹ค๋งŒ product_id๋Š” ์ถ”๊ฐ€ ๋น„์šฉ์ด ๋‚จ์Šต๋‹ˆ๋‹ค.

1-3. ๋ณตํ•ฉ ์ธ๋ฑ์Šค (buyer_product_idx) ์‚ฌ์šฉ

EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE buyer_id = 123
  AND product_id = 123
ORDER BY id DESC
LIMIT 100;
actual time=0.444..0.529
Index lookup on orders using buyer_product_idx (buyer_id=123, product_id=123)
  • ์กฐํšŒ ์กฐ๊ฑด์ด ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์„ ๋‘ ์ปฌ๋Ÿผ ์ˆœ์„œ์™€ ์ผ์น˜ํ•˜์—ฌ ๊ฐ€์žฅ ํšจ์œจ์ ์ธ ๊ฒฝ๋กœ๋ฅผ ํƒ”์Šต๋‹ˆ๋‹ค.

2) ํŽ˜์ด์ง• ์„ฑ๋Šฅ ๋น„๊ต

2-0. ์ƒํƒœ ํ•„ํ„ฐ ์ธ๋ฑ์Šค ๋น„ํšจ์œจ (์ €์นด๋””๋„๋ฆฌํ‹ฐ)

EXPLAIN ANALYZE
SELECT p.*
FROM product p
WHERE p.status = 'END_OF_SALE'
ORDER BY p.id DESC
LIMIT 20 OFFSET 20000;
์ธ๋ฑ์Šค ์ „: actual time=20.1..20.1
์ธ๋ฑ์Šค ํ›„: actual time=20.3..20.3
  • status = 'END_OF_SALE' ์กฐ๊ฑด์ด ๋Œ€๋Ÿ‰ ๋งค์นญ๋˜๋Š” ๋ถ„ํฌ์—์„œ๋Š” ์ธ๋ฑ์Šค ์„ ํƒ๋„๊ฐ€ ๋‚ฎ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฒˆ ์‹ค์ธก์—์„œ๋Š” ์ธ๋ฑ์Šค ์ ์šฉ ์ „/ํ›„ ์ฐจ์ด๊ฐ€ ๊ฑฐ์˜ ์—†์—ˆ๊ณ (์•ฝ 1% ์ €ํ•˜), ์„ฑ๋Šฅ ๊ฐœ์„  ํšจ๊ณผ๊ฐ€ ํ™•์ธ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • ์ด ๊ตฌ๊ฐ„์˜ ๋ณ‘๋ชฉ์€ ์ƒํƒœ ์ธ๋ฑ์Šค๋ณด๋‹ค OFFSET ์Šค์บ” ๋น„์šฉ ์˜ํ–ฅ์ด ๋” ํฝ๋‹ˆ๋‹ค.

2-1. OFFSET ํŽ˜์ด์ง•

EXPLAIN ANALYZE
SELECT p.*
FROM product p
ORDER BY p.id DESC
LIMIT 20 OFFSET 20000;
actual time=10.2..10.2 rows=20 loops=1
  • ํฐ OFFSET์„ ๊ฑด๋„ˆ๋›ฐ๊ธฐ ์œ„ํ•ด ๋ถˆํ•„์š”ํ•œ ํ–‰์„ ๋งŽ์ด ์ฝ์–ด์•ผ ํ•˜๋ฏ€๋กœ ๋น„์šฉ์ด ํฝ๋‹ˆ๋‹ค.

2-2. ์ปค์„œ ํŽ˜์ด์ง•

EXPLAIN ANALYZE
SELECT p.*
FROM product p
WHERE p.id < 20000
ORDER BY p.id DESC
LIMIT 20;
actual time=0.379..0.389 rows=20 loops=1
Index range scan on p using PRIMARY over (id < 20000)
  • PK ๋ฒ”์œ„๋ฅผ ์ง์ ‘ ์ขํ˜€ ์ฝ๊ธฐ ๋•Œ๋ฌธ์— deep offset ๋น„์šฉ์„ ํ”ผํ•ฉ๋‹ˆ๋‹ค.
  • ๋™์ผ ์กฐ๊ฑด ๊ธฐ์ค€ OFFSET(10.2ms) ๋Œ€๋น„ ์•ฝ 96.2% ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

3) ๊ฒฐ๋ก 

  • ์ฃผ๋ฌธ ์กฐํšŒ๋Š” orders(buyer_id, product_id) ๋ณตํ•ฉ ์ธ๋ฑ์Šค๊ฐ€ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.
  • ๋‹จ์ผ ์ธ๋ฑ์Šค(buyer_id)๋„ ์˜๋ฏธ ์žˆ๋Š” ๊ฐœ์„ ์€ ์žˆ์ง€๋งŒ, ๋ณตํ•ฉ ์ธ๋ฑ์Šค๊ฐ€ ์ถ”๊ฐ€ ์ด์ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ํŽ˜์ด์ง•์€ deep offset ๊ตฌ๊ฐ„์—์„œ ์ปค์„œ ๊ธฐ๋ฐ˜์ด ํ›จ์”ฌ ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก  ์š”์•ฝ

  • ์ธ๋ฑ์Šค ์ ์šฉ ๊ฒฐ๊ณผ, ์ฃผ๋ฌธ ์กฐํšŒ ํ•ต์‹ฌ ๊ตฌ๊ฐ„์—์„œ ์ตœ๋Œ€ ์•ฝ 98.9% ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํŠนํžˆ orders(buyer_id, product_id) ๋ณตํ•ฉ ์ธ๋ฑ์Šค๋Š” ๋‹จ์ผ ์ธ๋ฑ์Šค ๋Œ€๋น„๋„ ์ถ”๊ฐ€ ๊ฐœ์„  ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ˜๋ฉด product.status๋Š” ์ด๋ฒˆ ์‹ค์ธก์—์„œ ์ธ๋ฑ์Šค ์ „/ํ›„๊ฐ€ ๊ฑฐ์˜ ๋™์ผํ•ด, ์ธ๋ฑ์Šค ํšจ๊ณผ๊ฐ€ ํฌ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • ํŽ˜์ด์ง•์€ deep offset ๊ตฌ๊ฐ„์—์„œ ์ปค์„œ ๊ธฐ๋ฐ˜(id < cursor)์ด ์ผ๋ฐ˜ OFFSET ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ์•ˆ์ •์ ์ธ ์„ฑ๋Šฅ์„ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

CQRS ๊ด€์  ์ธํ”„๋ผ ์š”์•ฝ

  • ์กฐํšŒ(Read)๋Š” ๋ฆฌํ”Œ๋ฆฌ์นด DB(์Šฌ๋ ˆ์ด๋ธŒ)๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ์“ฐ๊ธฐ(Write)๋Š” ๋งˆ์Šคํ„ฐ DB์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ๊ตฌ์กฐ๋Š” ์ฝ๊ธฐ ๋ถ€ํ•˜ ๋ถ„์‚ฐ, ํŠธ๋ž˜ํ”ฝ ํ”ผํฌ ๋Œ€์‘, ์ˆ˜ํ‰ ํ™•์žฅ์„ฑ ํ™•๋ณด์— ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋™์‹œ์„ฑ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ (300๊ฑด)

StockFacadeTest์—์„œ ๋™์ผํ•œ ์žฌ๊ณ (quantity=300)์— ๋Œ€ํ•ด 300๊ฐœ ์š”์ฒญ์„ ๋™์‹œ์— ๋ฐœ์ƒ์‹œ์ผœ, ๋ฝ ์ „๋žต๋ณ„ ์žฌ๊ณ  ์ฐจ๊ฐ ๊ฒฐ๊ณผ์™€ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ๋น„๊ตํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ: StockFacadeTest.java
  • ์‹คํ–‰ ๋ฐฉ์‹: ExecutorService(100 threads) + CountDownLatch(300)
  • ๊ฒ€์ฆ ์กฐ๊ฑด: ๋ชจ๋“  ์š”์ฒญ ์™„๋ฃŒ ํ›„ ์žฌ๊ณ ๊ฐ€ 0์ธ์ง€ ๊ฒ€์ฆ

์‹ค์ธก ๊ฒฐ๊ณผ

๊ตฌ๋ถ„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„
๋น„๊ด€์  ๋ฝ (Pessimistic Lock) 1.51์ดˆ
๋‚™๊ด€์  ๋ฝ (Optimistic Lock) 16.61์ดˆ
  • ๋™์ผ ์กฐ๊ฑด์—์„œ ๋น„๊ด€์  ๋ฝ์ด ๋‚™๊ด€์  ๋ฝ ๋Œ€๋น„ ์•ฝ 11๋ฐฐ ๋น ๋ฅด๊ฒŒ ์ธก์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ ๊ธฐ๋ฐ˜ ๋ถ„์„

1) ๋น„๊ด€์  ๋ฝ

  • ํ˜ธ์ถœ ๊ฒฝ๋กœ:
    • StockFacade.decreaseWithPessimistic(...)
    • StockServiceImpl.decreaseWithPessimistic(...)
    • StockRepository.findByIdWithPessimisticLock(...)
  • ๊ตฌํ˜„ ํฌ์ธํŠธ:
    • StockRepository์—์„œ @Lock(LockModeType.PESSIMISTIC_WRITE) ์‚ฌ์šฉ
    • DB ๋ ˆ๋ฒจ์—์„œ ์“ฐ๊ธฐ ๋ฝ์„ ๋จผ์ € ์ ์œ ํ•˜์—ฌ ์ถฉ๋Œ์„ ์ง๋ ฌํ™”

2) ๋‚™๊ด€์  ๋ฝ

  • ํ˜ธ์ถœ ๊ฒฝ๋กœ:
    • StockFacade.decreaseWithOptimistic(...)
    • StockServiceImpl.decreaseWithOptimistic(...)
    • StockRepository.findByIdWithOptimisticLock(...)
  • ๊ตฌํ˜„ ํฌ์ธํŠธ:
    • Stock ์—”ํ‹ฐํ‹ฐ์˜ @Version ํ•„๋“œ ๊ธฐ๋ฐ˜ ์ถฉ๋Œ ๊ฐ์ง€
    • ์ถฉ๋Œ ๋ฐœ์ƒ ์‹œ ์žฌ์‹œ๋„(backoff) ๋กœ์ง ์ˆ˜ํ–‰
    • StockFacade์˜ ์žฌ์‹œ๋„ ๋ฃจํ”„(maxRetries=20, sleep 50ms ์ง€์ˆ˜ ๋ฐฑ์˜คํ”„) + StockServiceImpl ๋‚ด๋ถ€ ์žฌ์‹œ๋„ ๋Œ€๊ธฐ(Thread.sleep(100))๋กœ ์ธํ•ด ๊ณ ๊ฒฝํ•ฉ ์ƒํ™ฉ์—์„œ ๋ˆ„์  ์ง€์—ฐ์ด ์ปค์ง

ํ•ด์„

  • ๋ณธ ์‹œ๋‚˜๋ฆฌ์˜ค์ฒ˜๋Ÿผ ๋™์ผ row์— ๋Œ€ํ•œ ๊ณ ๊ฒฝํ•ฉ(300๊ฑด ๋™์‹œ ๊ฐฑ์‹ )์—์„œ๋Š”, ์ถฉ๋Œ ์ดํ›„ ์žฌ์‹œ๋„ ๋น„์šฉ์ด ํฐ ๋‚™๊ด€์  ๋ฝ๋ณด๋‹ค ๋น„๊ด€์  ๋ฝ์ด ๋” ์œ ๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ˜๋Œ€๋กœ, ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ๊ณ  ์ฝ๊ธฐ ๋น„์ค‘์ด ๋†’์€ ํŠธ๋ž˜ํ”ฝ์—์„œ๋Š” ๋‚™๊ด€์  ๋ฝ์ด ์œ ๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ํŠธ๋ž˜ํ”ฝ ํŠน์„ฑ์— ๋”ฐ๋ผ ๋ฝ ์ „๋žต์„ ๋ถ„๋ฆฌ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

์žฌํ˜„ ๋ฐฉ๋ฒ•

์‚ฌ์ „ ์ค€๋น„:

  • Redis/MySQL ์‹คํ–‰ ํ•„์š” (docker-compose.yml ๊ธฐ์ค€)

์‹คํ–‰ ์˜ˆ์‹œ:

./gradlew test --tests "sample.market.application.product.stock.StockFacadeTest"

Architecture Layers

Layer ๊ฐ„์˜ ์ฐธ์กฐ ๊ด€๊ณ„์—์„œ๋Š” ๋‹จ๋ฐฉํ–ฅ ์˜์กด ์œ ์ง€๋ฅผ ํ†ตํ•ด ํ™•์žฅ์— ์œ ์—ฐํ•œ ์•„ํ‚คํ…์ฒ˜๋กœ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ„๊ฒฐํ•˜๊ณ  ์ฝ๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ฑ…์ž„๊ณผ ์—ญํ• ์„ ์ž˜ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.


Layer ๊ฐ„ ์ฐธ์กฐ ๊ด€๊ณ„

  • application๊ณผ infrastructure ๋Š” domain Layer ๋ฅผ ๋ฐ”๋ผ๋ณด๊ฒŒ ํ•˜๊ณ  ์–‘๋ฐฉํ–ฅ ์ฐธ์กฐ๋Š” ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Application Layer

  • transaction์œผ๋กœ ๋ฌถ์—ฌ์•ผ ํ•˜๋Š” ๋„๋ฉ”์ธ ๋กœ์ง๊ณผ ๊ทธ๋ ‡์ง€ ์•Š๋Š” ๋กœ์ง์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด ํšŒ์›๊ฐ€์ž… ํ›„ ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต ์ด๋ฉ”์ผ ๋ฐœ์†ก์˜ ๊ฒฝ์šฐ ์ด๋ฉ”์ผ ๋ฐœ์†ก์ธ ์™ธ๋ถ€ ์„œ๋น„์Šค call์€ ํšŒ์›๊ฐ€์ž… ๋„๋ฉ”์ธ ๋กœ์ง์— ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ transaction์—์„œ ๋ถ„๋ฆฌ์‹œํ‚ค๋ฉฐ ํšŒ์›๊ฐ€์ž… ๋„๋ฉ”์ธ ๋กœ์ง์€ ์™ธ๋ถ€ ์„œ๋น„์Šค call ์„ฑ๊ณต / ์‹คํŒจ ์—ฌ๋ถ€์— ๋Œ€ํ•ด ํฌ๊ฒŒ ๋ฏผ๊ฐํ•˜์ง€ ์•Š๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค.

Domain Layer

  • domain Layer๋Š” low level์˜ ๊ธฐ์ˆ ์— ๊ด€๊ณ„์—†์ด ๋…๋ฆฝ์ ์œผ๋กœ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • domain layer ์—์„œ๋Š” ๋„๋ฉ”์ธ ๋กœ์ง์˜ ํ๋ฆ„์„ ํ‘œํ˜„ํ•˜๊ณ  ๊ตฌํ˜„ํ•˜๋Š” Service ์™€ ServiceImpl ์ด ์žˆ์ง€๋งŒ ๊ทธ ์™ธ์˜ ์ƒ์„ธํ•œ ๊ตฌํ˜„์€ Reader, Store, Executor ๊ฐ™์€ interface ๋ฅผ ์„ ์–ธํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์ด์— ๋Œ€ํ•œ ์‹ค์ œ ๊ตฌํ˜„์ฒด๋Š” Infrastructure layer ์— ๋‘๊ณ  ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

Infrastructure Layer

  • domain layer ์— ์„ ์–ธ๋˜๊ณ  ์‚ฌ์šฉ๋˜๋Š” ์ถ”์ƒํ™”๋œ interface ๋ฅผ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜์—ฌ runtime ์‹œ์—๋Š” ์‹ค์ œ ๋กœ์ง์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • Service ๊ฐ„์˜ ์ฐธ์กฐ ๊ด€๊ณ„๋Š” ๋ง‰์•˜์ง€๋งŒ, Infrastructure layer ์—์„œ์˜ ๊ตฌํ˜„์ฒด ๊ฐ„์—๋Š” ์ฐธ์กฐ ๊ด€๊ณ„๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

Interfaces Layer

  • API ๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ์—๋Š” ์—†์–ด๋„ ๋˜๋Š” Request Parameter ๋Š” ์ œ๊ฑฐํ•˜๊ณ , ์™ธ๋ถ€์— ๋ฆฌํ„ดํ•˜ ๋Š” Response ๋„ ์ตœ์†Œํ•œ์„ ์œ ์ง€ํ•˜๋„๋ก ๋…ธ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • http, gRPC, ๋น„๋™๊ธฐ ๋ฉ”์‹œ์ง•๊ณผ ๊ฐ™์€ ์„œ๋น„์Šค๊ฐ„ ํ†ต์‹  ๊ธฐ์ˆ ์€ Interfaces layer ์—์„œ๋งŒ ์‚ฌ ์šฉ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

Interfaces

  • xxApiController
  • xxDto

Application Layer

  • xxFacade

Domain Layer

  • Entity
  • xxService
  • xxServiceImpl
  • xxReader
  • xxStore
  • xxCommand
  • xxInfo
  • xxManager
  • xxManagerImpl
  • xxMapper

Infrastructure

  • xxReaderImpl
  • xxRepository
  • xxStoreImpl

Exception ํ•ธ๋“ค๋ง

๊ฐ€๋…์„ฑ๊ณผ ํŽธ์˜๋ฅผ ์œ„ํ•ด ํ‘œ์ค€์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ž˜๋ชป๋œ ์ธ์ž ์ž…๋ ฅ์‹œ IllegalArgumentException ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์ž˜๋ชป๋œ ์ƒํƒœ์ผ ๊ฒฝ์šฐ IllegalStateException ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์‹œ์Šคํ…œ ์˜ˆ์™ธ ์ƒํ™ฉ (์ง‘์ค‘ ๋ชจ๋‹ˆํ„ฐ๋ง ์ฒ˜๋ฆฌ) ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์—๋Ÿฌ ์ƒํ™ฉ์„ GlobalControllerAdvice ์—์„œ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • ์‹œ์Šคํ…œ ์˜ˆ์™ธ / http status : 500 AND result : FAIL
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์—๋Ÿฌ / http status : 200 AND result : FAIL
  • ์ž˜๋ชป๋œ ์ธ์ž ์ž…๋ ฅ ์˜ˆ์™ธ / http status : 400 AND result : FAIL

์š”๊ตฌ์‚ฌํ•ญ

1๋‹จ๊ณ„

  • ์œ ์ €๋Š” ์ œํ’ˆ๋“ฑ๋ก์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋“ฑ๋ก๋œ ์ œํ’ˆ์—๋Š” "์ œํ’ˆ๋ช…", "๊ฐ€๊ฒฉ", "์˜ˆ์•ฝ์ƒํƒœ"๊ฐ€ ํฌํ•จ๋˜์–ด์•ผํ•˜๊ณ , ๋ชฉ๋ก์กฐํšŒ์™€ ์ƒ์„ธ์กฐํšŒ์‹œ์— ์˜ˆ์•ฝ์ƒํƒœ๋ฅผ ํฌํ•จํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • ์ œํ’ˆ์˜ ์ƒํƒœ๋Š” "ํŒ๋งค์ค‘", "์˜ˆ์•ฝ์ค‘", "์™„๋ฃŒ" ์„ธ๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • ๊ตฌ๋งค์ž๊ฐ€ ์ œํ’ˆ์˜ ์ƒ์„ธํŽ˜์ด์ง€์—์„œ ๊ตฌ๋งคํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ฑฐ๋ž˜๊ฐ€ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.
  • ํŒ๋งค์ž์™€ ๊ตฌ๋งค์ž๋Š” ์ œํ’ˆ์˜ ์ƒ์„ธ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋ฉด ๋‹น์‚ฌ์ž๊ฐ„์˜ ๊ฑฐ๋ž˜๋‚ด์—ญ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชจ๋“  ์‚ฌ์šฉ์ž๋Š” ๋‚ด๊ฐ€ "๊ตฌ๋งคํ•œ ์šฉํ’ˆ(๋‚ด๊ฐ€ ๊ตฌ๋งค์ž)"๊ณผ "์˜ˆ์•ฝ์ค‘์ธ ์šฉํ’ˆ(๋‚ด๊ฐ€ ๊ตฌ๋งค์ž/ํŒ๋งค์ž ๋ชจ๋‘)"์˜ ๋ชฉ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํŒ๋งค์ž๋Š” ๊ฑฐ๋ž˜์ง„ํ–‰์ค‘์ธ ๊ตฌ๋งค์ž์— ๋Œ€ํ•ด 'ํŒ๋งค์Šน์ธ'์„ ํ•˜๋Š” ๊ฒฝ์šฐ ๊ฑฐ๋ž˜๊ฐ€ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

2๋‹จ๊ณ„

  • ์ œํ’ˆ์— ์ˆ˜๋Ÿ‰์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ์ œํ’ˆ์ •๋ณด์— "์ œํ’ˆ๋ช…", "๊ฐ€๊ฒฉ", "์˜ˆ์•ฝ์ƒํƒœ", "์ˆ˜๋Ÿ‰"์ด ํฌํ•จ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค์ˆ˜์˜ ๊ตฌ๋งค์ž๊ฐ€ ํ•œ ์ œํ’ˆ์— ๋Œ€ํ•ด ๊ตฌ๋งคํ•˜๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (๋‹จ, ํ•œ ๋ช…์ด ๊ตฌ๋งคํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜๋Ÿ‰์€ 1๊ฐœ๋ฟ์ž…๋‹ˆ๋‹ค.)
  • ๊ตฌ๋งคํ™•์ •์˜ ๋‹จ๊ณ„๊ฐ€ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ๊ตฌ๋งค์ž๋Š” ํŒ๋งค์ž๊ฐ€ ํŒ๋งค์Šน์ธํ•œ ์ œํ’ˆ์— ๋Œ€ํ•ด ๊ตฌ๋งคํ™•์ •์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฑฐ๋ž˜๊ฐ€ ์‹œ์ž‘๋˜๋Š” ๊ฒฝ์šฐ ์ˆ˜๋Ÿ‰์— ๋”ฐ๋ผ ์ œํ’ˆ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
  • ์ถ”๊ฐ€ ํŒ๋งค๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ˆ˜๋Ÿ‰์ด ๋‚จ์•„์žˆ๋Š” ๊ฒฝ์šฐ - ํŒ๋งค์ค‘
  • ์ถ”๊ฐ€ ํŒ๋งค๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ํ˜„์žฌ ๊ตฌ๋งคํ™•์ •์„ ๋Œ€๊ธฐํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ - ์˜ˆ์•ฝ์ค‘
  • ๋ชจ๋“  ์ˆ˜๋Ÿ‰์— ๋Œ€ํ•ด ๋ชจ๋“  ๊ตฌ๋งค์ž๊ฐ€ ๋ชจ๋‘ ๊ตฌ๋งคํ™•์ •ํ•œ ๊ฒฝ์šฐ - ์™„๋ฃŒ
  • "๊ตฌ๋งคํ•œ ์šฉํ’ˆ"๊ณผ "์˜ˆ์•ฝ์ค‘์ธ ์šฉํ’ˆ" ๋ชฉ๋ก์˜ ์ •๋ณด์—์„œ ๊ตฌ๋งคํ•˜๊ธฐ ๋‹น์‹œ์˜ ๊ฐ€๊ฒฉ ์ •๋ณด๊ฐ€ ๋‚˜ํƒ€๋‚˜์•ผํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ) ๊ตฌ๋งค์ž A๊ฐ€ ๊ตฌ๋งคํ•˜๊ธฐ ์š”์ฒญํ•œ ๋‹น์‹œ์˜ ์ œํ’ˆ B์˜ ๊ฐ€๊ฒฉ์ด 3000์›์ด์—ˆ๊ณ  ์ดํ›„์— 4000์›์œผ๋กœ ๋ฐ”๋€Œ์—ˆ๋‹ค ํ•˜๋”๋ผ๋„ ๋ชฉ๋ก์—์„œ๋Š” 3000์›์œผ๋กœ ๋‚˜ํƒ€๋‚˜์•ผํ•ฉ๋‹ˆ๋‹ค.
    • ์—ฌ๊ธฐ์„œ์˜ ์˜ˆ์•ฝ์ค‘์€ ์ƒํ’ˆ์˜ ์ƒํƒœ ์˜ˆ์•ฝ์ค‘์ด ์•„๋‹Œ ๊ฑฐ๋ž˜ ์ƒํƒœ์˜ ์˜ˆ์•ฝ์ค‘์„ ์˜๋ฏธํ•œ๋‹ค.

๊ณตํ†ต

๊ตฌ๋งค์ทจ์†Œ๋Š” ๊ณ ๋ คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. ์ž‘์„ฑํ•œ API์— ๋Œ€ํ•œ ๋ช…์„ธ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.


API ๋ช…์„ธ

์ œํ’ˆ

์ œํ’ˆ ๋“ฑ๋ก

[POST] /api/v1/products

{
  "name" : "sample",
  "price" : 123
}

๊ตฌ๋งคํ•œ ์ œํ’ˆ ์กฐํšŒ

GET /api/v1/products?buyerId&status=PURCHASED

buyerId: ๊ตฌ๋งค์ž Id

์˜ˆ์•ฝํ•œ ์ œํ’ˆ ๊ตฌ๋งค์ž๊ฐ€ ์กฐํšŒ

GET /api/v1/products?buyerId&status=RESERVED

buyerId: ๊ตฌ๋งค์ž Id

์˜ˆ์•ฝํ•œ ์ œํ’ˆ ํŒ๋งค์ž๊ฐ€ ์กฐํšŒ

GET /api/v1/products?sellerId&status=RESERVED

sellerId: ํŒ๋งค์ž Id


์ฃผ๋ฌธ

์ฃผ๋ฌธ ๋“ฑ๋ก

[POST] /api/v1/orders

{
  "buyerId" : 123,
  "productId" : 123
}

์ œํ’ˆ ๊ตฌ๋งค ์Šน์ธ

[POST] /api/v1/orders/approve

{
  "sellerId" : 123,
  "productId" : 123
}

์ œํ’ˆ ๊ตฌ๋งค ํ™•์ •

[POST] /api/v1/orders/complete

{
  "sellerId" : 123,
  "productId" : 123,
  "orderId" : 5000
}

์ฃผ๋ฌธ ์กฐํšŒ

[GET] /api/v1/orders?buyerId=123

buyerId: ๊ตฌ๋งค์ž Id


์œ ์ €

์œ ์ € ๋“ฑ๋ก

[POST] /api/v1/users

{
  "email" : "sample@gmail.com",
  "username" : "username",
  "password" : "password"
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages