Skip to content

MSA / DDD / Event Storming example on food delivery service

License

Notifications You must be signed in to change notification settings

msa-ez/example-library

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

37 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

์ฃผ์ œ - ๋„์„œ๊ด€ ์‹œ์Šคํ…œ

๋„์„œ๊ด€์˜ ์ฑ… ๋Œ€์—ฌ ๋ฐ ์˜ˆ์•ฝ, ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.

๊ตฌํ˜„ Repository

์ด 5๊ฐœ

  1. https://github.com/Juyounglee95/bookRental
  2. https://github.com/Juyounglee95/gateway
  3. https://github.com/Juyounglee95/bookManagement
  4. https://github.com/Juyounglee95/point
  5. https://github.com/Juyounglee95/view

์„œ๋น„์Šค ์‹œ๋‚˜๋ฆฌ์˜ค

๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ

  1. ๊ด€๋ฆฌ์ž๋Š” ๋„์„œ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
  2. ์‚ฌ์šฉ์ž๋Š” ๋„์„œ๋ฅผ ์˜ˆ์•ฝํ•œ๋‹ค.
  3. ๋„์„œ๋ฅผ ์˜ˆ์•ฝ ์‹œ์—๋Š” ํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. 3-1. ์˜ˆ์•ฝ ์ทจ์†Œ ์‹œ์—๋Š” ํฌ์ธํŠธ๊ฐ€ ๋ฐ˜๋‚ฉ๋œ๋‹ค.
  4. ์‚ฌ์šฉ์ž๋Š” ๋„์„œ๋ฅผ ๋ฐ˜๋‚ฉํ•œ๋‹ค.

๋น„๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ

  1. ํŠธ๋žœ์žญ์…˜
    1. ๊ฒฐ์ œ๊ฐ€ ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋Œ€์—ฌํ•  ์ˆ˜ ์—†๋‹ค. Sync ํ˜ธ์ถœ
  2. ์žฅ์• ๊ฒฉ๋ฆฌ
    1. ๋„์„œ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋”๋ผ๋„ ๋Œ€์—ฌ/์˜ˆ์•ฝ์€ 365์ผ 24์‹œ๊ฐ„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค Async (event-driven), Eventual Consistency
    2. ๊ฒฐ์ œ์‹œ์Šคํ…œ์ด ๊ณผ์ค‘๋˜๋ฉด ์‚ฌ์šฉ์ž๋ฅผ ์ž ์‹œ๋™์•ˆ ๋ฐ›์ง€ ์•Š๊ณ  ์ž ์‹œํ›„์— ๊ฒฐ์ œํ•˜๋„๋ก ์œ ๋„ํ•œ๋‹ค Circuit breaker, fallback
  3. ์„ฑ๋Šฅ
    1. ์‚ฌ์šฉ์ž๋Š” ์ „์ฒด ๋„์„œ ๋ชฉ๋ก์„ ํ™•์ธํ•˜์—ฌ ์ „์ฒด ๋„์„œ์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค. CQRS

์ฒดํฌํฌ์ธํŠธ

  • ๋ถ„์„ ์„ค๊ณ„

    • ์ด๋ฒคํŠธ์Šคํ† ๋ฐ:

      • ์Šคํ‹ฐ์ปค ์ƒ‰์ƒ๋ณ„ ๊ฐ์ฒด์˜ ์˜๋ฏธ๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์—ฌ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜์™€์˜ ์—ฐ๊ณ„ ์„ค๊ณ„์— ์ ์ ˆํžˆ ๋ฐ˜์˜ํ•˜๊ณ  ์žˆ๋Š”๊ฐ€?
      • ๊ฐ ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๊ฐ€ ์˜๋ฏธ์žˆ๋Š” ์ˆ˜์ค€์œผ๋กœ ์ •์˜๋˜์—ˆ๋Š”๊ฐ€?
      • ์–ด๊ทธ๋ฆฌ๊ฒŒ์ž‡: Command์™€ Event ๋“ค์„ ACID ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์˜ Aggregate ๋กœ ์ œ๋Œ€๋กœ ๋ฌถ์—ˆ๋Š”๊ฐ€?
      • ๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ๊ณผ ๋น„๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ˆ„๋ฝ ์—†์ด ๋ฐ˜์˜ํ•˜์˜€๋Š”๊ฐ€?
    • ์„œ๋ธŒ ๋„๋ฉ”์ธ, ๋ฐ”์šด๋””๋“œ ์ปจํ…์ŠคํŠธ ๋ถ„๋ฆฌ

      • ํŒ€๋ณ„ KPI ์™€ ๊ด€์‹ฌ์‚ฌ, ์ƒ์ดํ•œ ๋ฐฐํฌ์ฃผ๊ธฐ ๋“ฑ์— ๋”ฐ๋ฅธ ย Sub-domain ์ด๋‚˜ Bounded Context ๋ฅผ ์ ์ ˆํžˆ ๋ถ„๋ฆฌํ•˜์˜€๊ณ  ๊ทธ ๋ถ„๋ฆฌ ๊ธฐ์ค€์˜ ํ•ฉ๋ฆฌ์„ฑ์ด ์ถฉ๋ถ„ํžˆ ์„ค๋ช…๋˜๋Š”๊ฐ€?
        • ์ ์–ด๋„ 3๊ฐœ ์ด์ƒ ์„œ๋น„์Šค ๋ถ„๋ฆฌ
      • ํด๋ฆฌ๊ธ€๋ž ์„ค๊ณ„: ๊ฐ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋“ค์˜ ๊ตฌํ˜„ ๋ชฉํ‘œ์™€ ๊ธฐ๋Šฅ ํŠน์„ฑ์— ๋”ฐ๋ฅธ ๊ฐ์ž์˜ ๊ธฐ์ˆ  Stack ๊ณผ ์ €์žฅ์†Œ ๊ตฌ์กฐ๋ฅผ ๋‹ค์–‘ํ•˜๊ฒŒ ์ฑ„ํƒํ•˜์—ฌ ์„ค๊ณ„ํ•˜์˜€๋Š”๊ฐ€?
      • ์„œ๋น„์Šค ์‹œ๋‚˜๋ฆฌ์˜ค ์ค‘ ACID ํŠธ๋žœ์žญ์…˜์ด ํฌ๋ฆฌํ‹ฐ์ปฌํ•œ Use ์ผ€์ด์Šค์— ๋Œ€ํ•˜์—ฌ ๋ฌด๋ฆฌํ•˜๊ฒŒ ์„œ๋น„์Šค๊ฐ€ ๊ณผ๋‹คํ•˜๊ฒŒ ์กฐ๋ฐ€ํžˆ ๋ถ„๋ฆฌ๋˜์ง€ ์•Š์•˜๋Š”๊ฐ€?
    • ์ปจํ…์ŠคํŠธ ๋งคํ•‘ / ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ์•„ํ‚คํ…์ฒ˜

      • ์—…๋ฌด ์ค‘์š”์„ฑ๊ณผย  ๋„๋ฉ”์ธ๊ฐ„ ์„œ์—ด์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? (Core, Supporting, General Domain)
      • Request-Response ๋ฐฉ์‹๊ณผ ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ๋ฐฉ์‹์„ ๊ตฌ๋ถ„ํ•˜์—ฌ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?
      • ์žฅ์• ๊ฒฉ๋ฆฌ: ์„œํฌํŒ… ์„œ๋น„์Šค๋ฅผ ์ œ๊ฑฐ ํ•˜์—ฌ๋„ ๊ธฐ์กด ์„œ๋น„์Šค์— ์˜ํ–ฅ์ด ์—†๋„๋ก ์„ค๊ณ„ํ•˜์˜€๋Š”๊ฐ€?
      • ์‹ ๊ทœ ์„œ๋น„์Šค๋ฅผ ์ถ”๊ฐ€ ํ•˜์˜€์„๋•Œ ๊ธฐ์กด ์„œ๋น„์Šค์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜ํ–ฅ์ด ์—†๋„๋ก ์„ค๊ณ„(์—ด๋ ค์žˆ๋Š” ์•„ํ‚คํƒ์ฒ˜)ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?
      • ์ด๋ฒคํŠธ์™€ ํด๋ฆฌ์‹œ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ Correlation-key ์—ฐ๊ฒฐ์„ ์ œ๋Œ€๋กœ ์„ค๊ณ„ํ•˜์˜€๋Š”๊ฐ€?
    • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜

      • ์„ค๊ณ„ ๊ฒฐ๊ณผ์— ๋”ฐ๋ฅธ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ œ๋Œ€๋กœ ๊ทธ๋ ธ๋Š”๊ฐ€?
  • ๊ตฌํ˜„

    • [DDD] ๋ถ„์„๋‹จ๊ณ„์—์„œ์˜ ์Šคํ‹ฐ์ปค๋ณ„ ์ƒ‰์ƒ๊ณผ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜์— ๋”ฐ๋ผ ๊ตฌํ˜„์ฒด๊ฐ€ ๋งคํ•‘๋˜๊ฒŒ ๊ฐœ๋ฐœ๋˜์—ˆ๋Š”๊ฐ€?

      • Entity Pattern ๊ณผ Repository Pattern ์„ ์ ์šฉํ•˜์—ฌ JPA ๋ฅผ ํ†ตํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์–ด๋Œ‘ํ„ฐ๋ฅผ ๊ฐœ๋ฐœํ•˜์˜€๋Š”๊ฐ€
      • [ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜] REST Inbound adaptor ์ด์™ธ์— gRPC ๋“ฑ์˜ Inbound Adaptor ๋ฅผ ์ถ”๊ฐ€ํ•จ์— ์žˆ์–ด์„œ ๋„๋ฉ”์ธ ๋ชจ๋ธ์˜ ์†์ƒ์„ ์ฃผ์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด ํ”„๋กœํ† ์ฝœ์— ๊ธฐ์กด ๊ตฌํ˜„์ฒด๋ฅผ ์ ์‘์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š”๊ฐ€?
      • ๋ถ„์„๋‹จ๊ณ„์—์„œ์˜ ์œ ๋น„์ฟผํ„ฐ์Šค ๋žญ๊ท€์ง€ (์—…๋ฌดํ˜„์žฅ์—์„œ ์“ฐ๋Š” ์šฉ์–ด) ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์„œ์ˆ ๋˜์—ˆ๋Š”๊ฐ€?
    • Request-Response ๋ฐฉ์‹์˜ ์„œ๋น„์Šค ์ค‘์‹ฌ ์•„ํ‚คํ…์ฒ˜ ๊ตฌํ˜„

      • ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๊ฐ„ Request-Response ํ˜ธ์ถœ์— ์žˆ์–ด ๋Œ€์ƒ ์„œ๋น„์Šค๋ฅผ ์–ด๋– ํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฐพ์•„์„œ ํ˜ธ์ถœ ํ•˜์˜€๋Š”๊ฐ€? (Service Discovery, REST, FeignClient)
      • ์„œํ‚ท๋ธŒ๋ ˆ์ด์ปค๋ฅผ ํ†ตํ•˜์—ฌย  ์žฅ์• ๋ฅผ ๊ฒฉ๋ฆฌ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š”๊ฐ€?
    • ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ์•„ํ‚คํ…์ฒ˜์˜ ๊ตฌํ˜„

      • ์นดํ”„์นด๋ฅผ ์ด์šฉํ•˜์—ฌ PubSub ์œผ๋กœ ํ•˜๋‚˜ ์ด์ƒ์˜ ์„œ๋น„์Šค๊ฐ€ ์—ฐ๋™๋˜์—ˆ๋Š”๊ฐ€?
      • Correlation-key: ๊ฐ ์ด๋ฒคํŠธ ๊ฑด (๋ฉ”์‹œ์ง€)๊ฐ€ ์–ด๋– ํ•œ ํด๋ฆฌ์‹œ๋ฅผ ์ฒ˜๋ฆฌํ• ๋•Œ ์–ด๋–ค ๊ฑด์— ์—ฐ๊ฒฐ๋œ ์ฒ˜๋ฆฌ๊ฑด์ธ์ง€๋ฅผ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ Correlation-key ์—ฐ๊ฒฐ์„ ์ œ๋Œ€๋กœ ๊ตฌํ˜„ ํ•˜์˜€๋Š”๊ฐ€?
      • Message Consumer ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๊ฐ€ ์žฅ์• ์ƒํ™ฉ์—์„œ ์ˆ˜์‹ ๋ฐ›์ง€ ๋ชปํ–ˆ๋˜ ๊ธฐ์กด ์ด๋ฒคํŠธ๋“ค์„ ๋‹ค์‹œ ์ˆ˜์‹ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฐ€?
      • Scaling-out: Message Consumer ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์˜ Replica ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์„๋•Œ ์ค‘๋ณต์—†์ด ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€
      • CQRS: Materialized View ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ, ํƒ€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์˜ ๋ฐ์ดํ„ฐ ์›๋ณธ์— ์ ‘๊ทผ์—†์ด(Composite ์„œ๋น„์Šค๋‚˜ ์กฐ์ธSQL ๋“ฑ ์—†์ด) ๋„ ๋‚ด ์„œ๋น„์Šค์˜ ํ™”๋ฉด ๊ตฌ์„ฑ๊ณผ ์žฆ์€ ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•œ๊ฐ€?
    • ํด๋ฆฌ๊ธ€๋ž ํ”Œ๋กœ๊ทธ๋ž˜๋ฐ

      • ๊ฐ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋“ค์ด ํ•˜๋‚˜์ด์ƒ์˜ ๊ฐ์ž์˜ ๊ธฐ์ˆ  Stack ์œผ๋กœ ๊ตฌ์„ฑ๋˜์—ˆ๋Š”๊ฐ€?
      • ๊ฐ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋“ค์ด ๊ฐ์ž์˜ ์ €์žฅ์†Œ ๊ตฌ์กฐ๋ฅผ ์ž์œจ์ ์œผ๋กœ ์ฑ„ํƒํ•˜๊ณ  ๊ฐ์ž์˜ ์ €์žฅ์†Œ ์œ ํ˜• (RDB, NoSQL, File System ๋“ฑ)์„ ์„ ํƒํ•˜์—ฌ ๊ตฌํ˜„ํ•˜์˜€๋Š”๊ฐ€?
    • API ๊ฒŒ์ดํŠธ์›จ์ด

      • API GW๋ฅผ ํ†ตํ•˜์—ฌ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋“ค์˜ ์ง‘์ž…์ ์„ ํ†ต์ผํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?
      • ๊ฒŒ์ดํŠธ์›จ์ด์™€ ์ธ์ฆ์„œ๋ฒ„(OAuth), JWT ํ† ํฐ ์ธ์ฆ์„ ํ†ตํ•˜์—ฌ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋“ค์„ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?
  • ์šด์˜

    • SLA ์ค€์ˆ˜
      • ์…€ํ”„ํž๋ง: Liveness Probe ๋ฅผ ํ†ตํ•˜์—ฌ ์–ด๋– ํ•œ ์„œ๋น„์Šค์˜ health ์ƒํƒœ๊ฐ€ ์ง€์†์ ์œผ๋กœ ์ €ํ•˜๋จ์— ๋”ฐ๋ผ ์–ด๋– ํ•œ ์ž„๊ณ„์น˜์—์„œ pod ๊ฐ€ ์žฌ์ƒ๋˜๋Š” ๊ฒƒ์„ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?
      • ์„œํ‚ท๋ธŒ๋ ˆ์ด์ปค, ๋ ˆ์ดํŠธ๋ฆฌ๋ฐ‹ ๋“ฑ์„ ํ†ตํ•œ ์žฅ์• ๊ฒฉ๋ฆฌ์™€ ์„ฑ๋Šฅํšจ์œจ์„ ๋†’ํž ์ˆ˜ ์žˆ๋Š”๊ฐ€?
      • ์˜คํ† ์Šค์ผ€์ผ๋Ÿฌ (HPA) ๋ฅผ ์„ค์ •ํ•˜์—ฌ ํ™•์žฅ์  ์šด์˜์ด ๊ฐ€๋Šฅํ•œ๊ฐ€?
      • ๋ชจ๋‹ˆํ„ฐ๋ง, ์•จ๋ŸฟํŒ…:
    • ๋ฌด์ •์ง€ ์šด์˜ CI/CD (10)
      • Readiness Probe ์˜ ์„ค์ •๊ณผ Rolling update์„ ํ†ตํ•˜์—ฌ ์‹ ๊ทœ ๋ฒ„์ „์ด ์™„์ „ํžˆ ์„œ๋น„์Šค๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ์ผ๋•Œ ์‹ ๊ทœ๋ฒ„์ „์˜ ์„œ๋น„์Šค๋กœ ์ „ํ™˜๋จ์„ siege ๋“ฑ์œผ๋กœ ์ฆ๋ช…
      • Contract Test : ์ž๋™ํ™”๋œ ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•˜์—ฌ ๊ตฌํ˜„ ์˜ค๋ฅ˜๋‚˜ API ๊ณ„์•ฝ์œ„๋ฐ˜๋ฅผ ๋ฏธ๋ฆฌ ์ฐจ๋‹จ ๊ฐ€๋Šฅํ•œ๊ฐ€?

๋ถ„์„/์„ค๊ณ„

์ด๋ฒคํŠธ ๋„์ถœ

image

์•กํ„ฐ, ์ปค๋งจ๋“œ ๋ถ€์ฐฉํ•˜์—ฌ ์ฝ๊ธฐ ์ข‹๊ฒŒ

image

์–ด๊ทธ๋ฆฌ๊ฒŒ์ž‡์œผ๋กœ ๋ฌถ๊ธฐ

image

๋ฐ”์šด๋””๋“œ ์ปจํ…์ŠคํŠธ๋กœ ๋ฌถ๊ธฐ

image

- ๋„๋ฉ”์ธ ์„œ์—ด ๋ถ„๋ฆฌ 
    - Core Domain:  bookRental, bookManagement : ํ•ต์‹ฌ ์„œ๋น„์Šค
    - Supporting Domain:   marketing, customer : ๊ฒฝ์Ÿ๋ ฅ์„ ๋‚ด๊ธฐ์œ„ํ•œ ์„œ๋น„์Šค
    - General Domain:   point : ๊ฒฐ์ œ์„œ๋น„์Šค๋กœ 3rd Party ์™ธ๋ถ€ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฒฝ์Ÿ๋ ฅ์ด ๋†’์Œ (ํ•‘ํฌ์ƒ‰์œผ๋กœ ์ดํ›„ ์ „ํ™˜ํ•  ์˜ˆ์ •)

ํด๋ฆฌ์‹œ ๋ถ€์ฐฉ

image

ํด๋ฆฌ์‹œ์˜ ์ด๋™๊ณผ ์ปจํ…์ŠคํŠธ ๋งคํ•‘ (์ ์„ ์€ Pub/Sub, ์‹ค์„ ์€ Req/Resp)

image

  • View Model ์ถ”๊ฐ€

๊ธฐ๋Šฅ์ /๋น„๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ์„ ์ปค๋ฒ„ํ•˜๋Š”์ง€ ๊ฒ€์ฆ

image

<๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ ๊ฒ€์ฆ>

  • ๊ด€๋ฆฌ์ž๋Š” ๋„์„œ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. ok
  • ์‚ฌ์šฉ์ž๋Š” ๋„์„œ๋ฅผ ์˜ˆ์•ฝํ•œ๋‹ค. ok
  • ๋„์„œ๋ฅผ ์˜ˆ์•ฝ ์‹œ์—๋Š” ํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ok
  • ์˜ˆ์•ฝ ์ทจ์†Œ ์‹œ์—๋Š” ํฌ์ธํŠธ๊ฐ€ ๋ฐ˜๋‚ฉ๋œ๋‹ค. ok
  • ์‚ฌ์šฉ์ž๋Š” ๋„์„œ๋ฅผ ๋ฐ˜๋‚ฉํ•œ๋‹ค. ok
  • ์‚ฌ์šฉ์ž๋Š” ์˜ˆ์•ฝ์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค (ok)
  • ์˜ˆ์•ฝ์ด ์ทจ์†Œ๋˜๋ฉด ํฌ์ธํŠธ๊ฐ€ ๋ฐ˜๋‚ฉ๋˜๊ณ , ๋„์„œ์˜ ์ƒํƒœ๊ฐ€ ์˜ˆ์•ฝ ์ทจ์†Œ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค (ok)
  • ์‚ฌ์šฉ์ž๋Š” ๋„์„œ์ƒํƒœ๋ฅผ ์ค‘๊ฐ„์ค‘๊ฐ„ ์กฐํšŒํ•œ๋‹ค (View-green sticker ์˜ ์ถ”๊ฐ€๋กœ ok)
  • ๋„์„œ๊ฐ€ ๋“ฑ๋ก/์˜ˆ์•ฝ/์˜ˆ์•ฝ์ทจ์†Œ/๋ฐ˜๋‚ฉ ์‹œ, ๋„์„œ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด ์ „์ฒด ๋„์„œ ๋ฆฌ์ŠคํŠธ์— ๋ฐ˜์˜๋œ๋‹ค. ์‚ฌ์šฉ์ž์™€ ๊ด€๋ฆฌ์ž ๋ชจ๋‘ ์ด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ok

๋น„๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๊ฒ€์ฆ

  • ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋ฅผ ๋„˜๋‚˜๋“œ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•œ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ
    • ๋„์„œ ์˜ˆ์•ฝ์‹œ ๊ฒฐ์ œ์ฒ˜๋ฆฌ: ์˜ˆ์•ฝ์™„๋ฃŒ์‹œ ํฌ์ธํŠธ ๊ฒฐ์ œ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด์„œ๋Š” Request-Response ๋ฐฉ์‹ ์ฒ˜๋ฆฌ
    • ๊ฒฐ์ œ ์™„๋ฃŒ์‹œ ๋„์„œ ์ƒํƒœ ๋ณ€๊ฒฝ: Eventual Consistency ๋ฐฉ์‹์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌํ•จ.
    • ๋‚˜๋จธ์ง€ ๋ชจ๋“  inter-microservice ํŠธ๋žœ์žญ์…˜: ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์˜ ์‹œ์ ์ด ํฌ๋ฆฌํ‹ฐ์ปฌํ•˜์ง€ ์•Š์€ ๋ชจ๋“  ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด๋ผ ํŒ๋‹จ, Eventual Consistency ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์ฑ„ํƒํ•จ.

ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ๋‹ค์ด์–ด๊ทธ๋žจ ๋„์ถœ

image

  • Chris Richardson, MSA Patterns ์ฐธ๊ณ ํ•˜์—ฌ Inbound adaptor์™€ Outbound adaptor๋ฅผ ๊ตฌ๋ถ„ํ•จ
  • ํ˜ธ์ถœ๊ด€๊ณ„์—์„œ PubSub ๊ณผ Req/Resp ๋ฅผ ๊ตฌ๋ถ„ํ•จ
  • ์„œ๋ธŒ ๋„๋ฉ”์ธ๊ณผ ๋ฐ”์šด๋””๋“œ ์ปจํ…์ŠคํŠธ์˜ ๋ถ„๋ฆฌ: ๊ฐ ํŒ€์˜ KPI ๋ณ„๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ด€์‹ฌ ๊ตฌํ˜„ ์Šคํ† ๋ฆฌ๋ฅผ ๋‚˜๋ˆ ๊ฐ€์ง

๊ตฌํ˜„:

๋ถ„์„/์„ค๊ณ„ ๋‹จ๊ณ„์—์„œ ๋„์ถœ๋œ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜์— ๋”ฐ๋ผ, ๊ฐ BC๋ณ„๋กœ ๋Œ€๋ณ€๋˜๋Š” ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋“ค์„ ์Šคํ”„๋ง๋ถ€ํŠธ๋กœ ๊ตฌํ˜„ํ•จ. ๊ตฌํ˜„ํ•œ ๊ฐ ์„œ๋น„์Šค๋ฅผ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค (๊ฐ์ž์˜ ํฌํŠธ๋„˜๋ฒ„๋Š” 8081 ~ 808n ์ด๋‹ค) bookManagement/ bookRental/ gateway/ point/ view/

cd bookManagement
mvn spring-boot:run

cd bookRental
mvn spring-boot:run 

cd gateway
mvn spring-boot:run  

cd point
mvn spring-boot:run

cd view
mvn spring-boot:run

DDD ์˜ ์ ์šฉ

  • ๊ฐ ์„œ๋น„์Šค๋‚ด์— ๋„์ถœ๋œ ํ•ต์‹ฌ Aggregate Root ๊ฐ์ฒด๋ฅผ Entity ๋กœ ์„ ์–ธ. ์ด๋•Œ ๊ฐ€๋Šฅํ•œ ํ˜„์—…์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์–ธ์–ด (์œ ๋น„์ฟผํ„ฐ์Šค ๋žญ๊ท€์ง€)๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•จ.
package library;

import javax.persistence.*;
import org.springframework.beans.BeanUtils;
import java.util.List;

@Entity
@Table(name="PointSystem_table")
public class PointSystem {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private Long bookId;
    private Long pointQty =(long)100;

    @PostPersist
    public void onPostPersist(){
        PointUsed pointUsed = new PointUsed(this);
        BeanUtils.copyProperties(this, pointUsed);
        pointUsed.publish();


    }

    public Long getBookId() {
        return bookId;
    }

    public void setBookId(Long bookId) {
        this.bookId = bookId;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    public Long getPointQty() {
        return pointQty;
    }

    public void setPointQty(Long pointQty) {
        this.pointQty = pointQty;
    }




}


  • Entity Pattern ๊ณผ Repository Pattern ์„ ์ ์šฉํ•˜์—ฌ JPA ๋ฅผ ํ†ตํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ์†Œ์Šค ์œ ํ˜• (RDB or NoSQL) ์— ๋Œ€ํ•œ ๋ณ„๋„์˜ ์ฒ˜๋ฆฌ๊ฐ€ ์—†๋„๋ก ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•˜์—ฌ Spring Data REST ์˜ RestRepository ๋ฅผ ์ ์šฉํ•˜์˜€๋‹ค
package library;

import org.springframework.data.repository.PagingAndSortingRepository;

public interface PointSystemRepository extends PagingAndSortingRepository<PointSystem, Long>{


}
  • ์ ์šฉ ํ›„ REST API ์˜ ํ…Œ์ŠคํŠธ
# bookManagement ์„œ๋น„์Šค์˜ ๋„์„œ ๋“ฑ๋ก์ฒ˜๋ฆฌ
http POST http://52.231.116.117:8080/bookManageSystems bookName="JPA"

# bookRental ์„œ๋น„์Šค์˜ ์˜ˆ์•ฝ์ฒ˜๋ฆฌ
http POST http://52.231.116.117:8080/bookRentalSystems/returned/1

# bookRental ์„œ๋น„์Šค์˜ ๋ฐ˜๋‚ฉ์ฒ˜๋ฆฌ
http POST http://52.231.116.117:8080/bookRentalSystems/reserve/1

# bookRental ์„œ๋น„์Šค์˜ ์˜ˆ์•ฝ์ทจ์†Œ์ฒ˜๋ฆฌ
http POST http://52.231.116.117:8080/bookRentalSystems/reserveCanceled/1

# ๋„์„œ ์ƒํƒœ ํ™•์ธ
http://52.231.116.117:8080/bookLists

๋™๊ธฐ์‹ ํ˜ธ์ถœ ๊ณผ ๋น„๋™๊ธฐ์‹

๋ถ„์„๋‹จ๊ณ„์—์„œ์˜ ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋กœ ์˜ˆ์•ฝ(bookRental)->๊ฒฐ์ œ(point) ๊ฐ„์˜ ํ˜ธ์ถœ์€ ๋™๊ธฐ์‹ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋กœ ํ•˜์˜€๋‹ค. ํ˜ธ์ถœ ํ”„๋กœํ† ์ฝœ์€ ์ด๋ฏธ ์•ž์„œ Rest Repository ์— ์˜ํ•ด ๋…ธ์ถœ๋˜์–ด์žˆ๋Š” REST ์„œ๋น„์Šค๋ฅผ FeignClient ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ˜ธ์ถœํ•˜๋„๋ก ํ•œ๋‹ค.

  • ๊ฒฐ์ œ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•˜์—ฌ Stub๊ณผ (FeignClient) ๋ฅผ ์ด์šฉํ•˜์—ฌ Service ๋Œ€ํ–‰ ์ธํ„ฐํŽ˜์ด์Šค (Proxy) ๋ฅผ ๊ตฌํ˜„
# (app) pointSystemService.java

@FeignClient(name="point", url="http://52.231.116.117:8080")
public interface PointSystemService {

    @RequestMapping(method= RequestMethod.POST, path="/pointSystems", consumes = "application/json")
    public void usePoints(@RequestBody PointSystem pointSystem);

}

  • ์˜ˆ์•ฝ์„ ๋ฐ›์€ ์งํ›„(@PostPersist) ๊ฒฐ์ œ๋ฅผ ์š”์ฒญํ•˜๋„๋ก ์ฒ˜๋ฆฌ -> BookRental์˜ ์ƒ์„ฑ์€ BookManageSystem์—์„œ ๋„์„œ๋ฅผ ๋“ฑ๋กํ•œ ์งํ›„ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Post์š”์ฒญ์œผ๋กœ ์˜ˆ์•ฝ์ด ๋“ค์–ด์˜จ ํ›„ ๊ฒฐ์ œ ์š”์ฒญํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•จ.
# BookRentalSystemController.java (Entity)

     @PostMapping("/bookRentalSystems/reserve/{id}")
     public void bookReserve(@PathVariable(value="id")Long id){
      PointSystem pointSystem = new PointSystem();
      pointSystem.setBookId(id);
      PointSystemService pointSystemService =  Application.applicationContext.
              getBean(library.external.PointSystemService.class);
      pointSystemService.usePoints(pointSystem);
  }
    }

๊ฒฐ์ œ๊ฐ€ ์ด๋ฃจ์–ด์ง„ ํ›„์— ๋„์„œ๋Œ€์—ฌ์‹œ์Šคํ…œ์œผ๋กœ ์ด๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ํ–‰์œ„๋Š” ๋™๊ธฐ์‹์ด ์•„๋‹ˆ๋ผ ๋น„ ๋™๊ธฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋„์„œ๋Œ€์—ฌ์‹œ์Šคํ…œ์˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•˜์—ฌ ๋„์„œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋Š” ๋ธ”๋กœํ‚น ๋˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ•œ๋‹ค.

  • ์ด๋ฅผ ์œ„ํ•˜์—ฌ ๊ฒฐ์ œ์ด๋ ฅ์— ๊ธฐ๋ก์„ ๋‚จ๊ธด ํ›„์— ๊ณง๋ฐ”๋กœ ๊ฒฐ์ œ์Šน์ธ์ด ๋˜์—ˆ๋‹ค๋Š” ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๋ฅผ ์นดํ”„์นด๋กœ ์†ก์ถœํ•œ๋‹ค(Publish)

#PointSystem.Java (Entity)
{
 @PostPersist
    public void onPostPersist(){
        PointUsed pointUsed = new PointUsed(this);
        BeanUtils.copyProperties(this, pointUsed);
        pointUsed.publish();


    }
}

๊ฒฐ์ œ ์™„๋ฃŒ ์ด๋ฒคํŠธ๋ฅผ ๋„์„œ๋Œ€์—ฌ์‹œ์Šคํ…œ์˜ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋ฐ›์•„, ๋„์„œ์˜ ์ƒํƒœ๋ฅผ ์˜ˆ์•ฝ์™„๋ฃŒ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

(BookRentalSystem) PolicyHandler.JAVA
{
    @StreamListener(KafkaProcessor.INPUT) //ํฌ์ธํŠธ ๊ฒฐ์ œ ์™„๋ฃŒ์‹œ
    public void wheneverPointUsed_ChangeStatus(@Payload PointUsed pointUsed){
        try {
            if (pointUsed.isMe()) {
                System.out.println("##### point use completed : " + pointUsed.toJson());
                BookRentalSystem bookRentalSystem = bookRentalSystemRepository.findById(pointUsed.getBookId()).get();
                bookRentalSystem.setBookStatus("Reserved Complete");
                bookRentalSystemRepository.save(bookRentalSystem);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

๋„์„œ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, Reserved๋ผ๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค.

BookRentalSystem.java (Entity)

{
   @PostUpdate
    public void bookStatusUpdate(){

        if(this.getBookStatus().equals("Returned")){
            Returned returned = new Returned(this);
            BeanUtils.copyProperties(this, returned);
            returned.publish();

        }else if(this.getBookStatus().equals("Canceled")){

            ReservationCanceled reservationCanceled = new ReservationCanceled(this);
            BeanUtils.copyProperties(this, reservationCanceled);
            reservationCanceled.publish();
        }else if(this.getBookStatus().equals("Reserved Complete")){
            Reserved reserved = new Reserved(this);
            BeanUtils.copyProperties(this, reserved);
            reserved.publish();
        }

    }
    
}

๊ฒฐ๊ณผ : ํฌ์ธํŠธ๊ฐ€ ์‚ฌ์šฉ๋œ ํ›„์—, ์˜ˆ์•ฝ์ด ์™„๋ฃŒ๋˜๋Š” ๊ฒƒ๊ณผ ๋„์„œ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒƒ์„ BookListViewํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

image

์šด์˜

CI/CD ์„ค์ •

๊ฐ ๊ตฌํ˜„์ฒด๋“ค์€ ๊ฐ์ž์˜ source repository ์— ๊ตฌ์„ฑ๋˜์—ˆ๊ณ , ์‚ฌ์šฉํ•œ CI/CD ํ”Œ๋žซํผ์€ azure๋ฅผ ์‚ฌ์šฉํ•˜์˜€์œผ๋ฉฐ, pipeline build script ๋Š” ๊ฐ ํ”„๋กœ์ ํŠธ ํด๋” ์ดํ•˜์— azure-pipeline.yml ์— ํฌํ•จ๋˜์—ˆ๋‹ค.

pipeline ๋™์ž‘ ๊ฒฐ๊ณผ

์•„๋ž˜ ์ด๋ฏธ์ง€๋Š” azure์˜ pipeline์— ๊ฐ๊ฐ์˜ ์„œ๋น„์Šค๋“ค์„ ์˜ฌ๋ ค, ์ฝ”๋“œ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋ ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋นŒ๋“œ/๋ฐฐํฌ ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

image

๊ทธ ๊ฒฐ๊ณผ kubernetes cluster์— ์•„๋ž˜์™€ ๊ฐ™์ด ์„œ๋น„์Šค๊ฐ€ ์˜ฌ๋ผ๊ฐ€์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

image

๋˜ํ•œ, ๊ธฐ๋Šฅ๋“ค๋„ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

<์ด๋ฒคํŠธ ๋‚ ๋ฆฌ๊ธฐ>

image image image image image

<๋™์ž‘ ๊ฒฐ๊ณผ>

image

์˜คํ† ์Šค์ผ€์ผ ์•„์›ƒ

  • ํฌ์ธํŠธ์„œ๋น„์Šค์— ๋Œ€ํ•œ replica ๋ฅผ ๋™์ ์œผ๋กœ ๋Š˜๋ ค์ฃผ๋„๋ก HPA ๋ฅผ ์„ค์ •ํ•œ๋‹ค. ์„ค์ •์€ CPU ์‚ฌ์šฉ๋Ÿ‰์ด 15ํ”„๋กœ๋ฅผ ๋„˜์–ด์„œ๋ฉด replica ๋ฅผ 10๊ฐœ๊นŒ์ง€ ๋Š˜๋ ค์ค€๋‹ค:
  • ์˜คํ† ์Šค์ผ€์ผ์ด ์–ด๋–ป๊ฒŒ ๋˜๊ณ  ์žˆ๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ๊ฑธ์–ด๋‘”๋‹ค:

image

  • ์›Œํฌ๋กœ๋“œ๋ฅผ 2๋ถ„ ๋™์•ˆ ๊ฑธ์–ด์ค€ ํ›„ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

image

๋ฌด์ •์ง€ ์žฌ๋ฐฐํฌ

Autoscaler์„ค์ •๊ณผ Readiness ์ œ๊ฑฐ๋ฅผ ํ•œ๋’ค, ๋ถ€ํ•˜๋ฅผ ๋„ฃ์—ˆ๋‹ค.

์ดํ›„ Readiness๋ฅผ ์ œ๊ฑฐํ•œ ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ๋ฐฐํฌ๋ฅผ ์‹œ์ž‘ํ–ˆ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜๋Š” ๊ฐ™๋‹ค.

image image

๋‹ค์‹œ Readiness ์„ค์ •์„ ๋„ฃ๊ณ  ๋ถ€ํ•˜๋ฅผ ๋„ฃ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋ฒ„์ „์œผ๋กœ ๋ฐฐํฌํ•œ ๋’ค ๊ทธ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

image image

๋ฐฐํฌ๊ธฐ๊ฐ„ ๋™์•ˆ Availability ๊ฐ€ ๋ณ€ํ™”์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด์ •์ง€ ์žฌ๋ฐฐํฌ๊ฐ€ ์„ฑ๊ณตํ•œ ๊ฒƒ์œผ๋กœ ํ™•์ธ๋จ.

About

MSA / DDD / Event Storming example on food delivery service

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages

  • Java 93.2%
  • Python 3.7%
  • Dockerfile 3.1%