BACKEND

[Architecture]ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ (Hexagonal Architecture

handcraft 2025. 9. 4. 13:14

๐Ÿ”ท ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜(Hexagonal Architecture)๋ž€?

  • ๋ชฉ์ : ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(๋„๋ฉ”์ธ, ์œ ์Šค์ผ€์ด์Šค)์„ ์™ธ๋ถ€ ๊ธฐ์ˆ (DB, UI, ๋ฉ”์‹œ์ง€ ํ, API ๋“ฑ)๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•ด์„œ ํ•ต์‹ฌ ๋กœ์ง์„ ์•ˆ์ •์ ์œผ๋กœ ๋ณดํ˜ธํ•˜๊ณ , ๊ธฐ์ˆ  ์š”์†Œ๋Š” ์‰ฝ๊ฒŒ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด
  • ๋‹ค๋ฅธ ์ด๋ฆ„: Ports and Adapters ํŒจํ„ด

๐Ÿงฉ ํ•ต์‹ฌ ๊ฐœ๋…

https://dzone.com/articles/hexagonal-architecture-in-java-2

  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”์–ด (Application Core)

    • ๋„๋ฉ”์ธ ๋ชจ๋ธ(์—”ํ‹ฐํ‹ฐ, ๊ฐ’ ๊ฐ์ฒด ๋“ฑ)
    • ์œ ์Šค์ผ€์ด์Šค(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค)
    • ์™ธ๋ถ€ ๊ธฐ์ˆ ์— ์ „ํ˜€ ์˜์กดํ•˜์ง€ ์•Š๋Š” ์ˆœ์ˆ˜ํ•œ ๋กœ์ง
  1. ํฌํŠธ (Ports)
    • ์ฝ”์–ด์™€ ์™ธ๋ถ€ ์„ธ๊ณ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์ถ”์ƒ ์ธํ„ฐํŽ˜์ด์Šค
    • ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋‰จ
      • ์ธ์ปค๋ฐ ํฌํŠธ (Incoming Port)
        → ์™ธ๋ถ€์—์„œ ์ฝ”์–ด๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉ (์˜ˆ: REST Controller๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” UseCase ์ธํ„ฐํŽ˜์ด์Šค)
      • ์•„์›ƒ๊ณ ์ž‰ ํฌํŠธ (Outgoing Port)
        → ์ฝ”์–ด์—์„œ ์™ธ๋ถ€๋กœ ๋‚˜๊ฐˆ ๋•Œ ์‚ฌ์šฉ (์˜ˆ: Repository ์ธํ„ฐํŽ˜์ด์Šค, ์™ธ๋ถ€ API ํ˜ธ์ถœ ์ธํ„ฐํŽ˜์ด์Šค)
  2. ์–ด๋Œ‘ํ„ฐ (Adapters)
    • ํฌํŠธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‹ค์ œ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ์ˆ ์  ์š”์†Œ
    • ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋‰จ
      • ๋“œ๋ผ์ด๋น™ ์–ด๋Œ‘ํ„ฐ (Driving Adapter)
        → ์ธ์ปค๋ฐ ํฌํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์™ธ๋ถ€ ๊ธฐ์ˆ  (์˜ˆ: Controller, CLI, Event Listener)
      • ๋“œ๋ฆฌ๋ธ ์–ด๋Œ‘ํ„ฐ (Driven Adapter)
        → ์•„์›ƒ๊ณ ์ž‰ ํฌํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์™ธ๋ถ€ ๊ธฐ์ˆ  (์˜ˆ: DB Repository, ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค, ์™ธ๋ถ€ API ํด๋ผ์ด์–ธํŠธ)

โš™๏ธ ๋™์ž‘ ํ๋ฆ„ ์˜ˆ์‹œ

  • ์‚ฌ์šฉ์ž๊ฐ€ **Controller(๋“œ๋ผ์ด๋น™ ์–ด๋Œ‘ํ„ฐ)**๋ฅผ ํ†ตํ•ด ์š”์ฒญ →
  • Controller๋Š” ์ธ์ปค๋ฐ ํฌํŠธ(UseCase ์ธํ„ฐํŽ˜์ด์Šค) ํ˜ธ์ถœ →
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”์–ด๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ˆ˜ํ–‰ →
  • ํ•„์š”ํ•˜๋‹ค๋ฉด ์•„์›ƒ๊ณ ์ž‰ ํฌํŠธ(Repository ์ธํ„ฐํŽ˜์ด์Šค) ํ˜ธ์ถœ →
  • **๋“œ๋ฆฌ๋ธ ์–ด๋Œ‘ํ„ฐ(DB, ์™ธ๋ถ€ API ๊ตฌํ˜„์ฒด)**๊ฐ€ ์‘๋‹ต →
  • ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ˜ํ™˜

๐Ÿ“‚ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ์˜ˆ์‹œ ๊ตฌ์กฐ (Java)

๐Ÿ“ฆ com.example.project
 โ”ฃ ๐Ÿ“ application            // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค (์œ ์Šค์ผ€์ด์Šค, ์ธ์ปค๋ฐ ํฌํŠธ)
 โ”‚  โ”ฃ ๐Ÿ“ port
 โ”‚  โ”‚  โ”ฃ ๐Ÿ“ in
 โ”‚  โ”‚  โ”‚  โ”— ๐Ÿ“„ CreateOrderUseCase.java
 โ”‚  โ”‚  โ”— ๐Ÿ“ out
 โ”‚  โ”‚     โ”— ๐Ÿ“„ OrderRepository.java
 โ”‚  โ”— ๐Ÿ“ service
 โ”‚     โ”— ๐Ÿ“„ CreateOrderService.java
 โ”‚
 โ”ฃ ๐Ÿ“ domain                 // ๋„๋ฉ”์ธ ๋ชจ๋ธ (์ˆœ์ˆ˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)
 โ”‚  โ”ฃ ๐Ÿ“ model
 โ”‚  โ”‚  โ”ฃ ๐Ÿ“„ Order.java
 โ”‚  โ”‚  โ”— ๐Ÿ“„ Product.java
 โ”‚  โ”— ๐Ÿ“ logic
 โ”‚     โ”— ๐Ÿ“„ OrderValidator.java
 โ”‚
 โ”ฃ ๐Ÿ“ adapter                // ์–ด๋Œ‘ํ„ฐ (ํฌํŠธ์˜ ์‹ค์ œ ๊ตฌํ˜„)
 โ”‚  โ”ฃ ๐Ÿ“ in
 โ”‚  โ”‚  โ”— ๐Ÿ“ web
 โ”‚  โ”‚     โ”— ๐Ÿ“„ OrderController.java
 โ”‚  โ”— ๐Ÿ“ out
 โ”‚     โ”— ๐Ÿ“ persistence
 โ”‚        โ”ฃ ๐Ÿ“ jpa
 โ”‚        โ”‚  โ”ฃ ๐Ÿ“„ OrderJpaEntity.java
 โ”‚        โ”‚  โ”ฃ ๐Ÿ“„ SpringDataOrderRepository.java
 โ”‚        โ”‚  โ”— ๐Ÿ“„ JpaOrderRepositoryAdapter.java
 โ”‚        โ”— ๐Ÿ“ external
 โ”‚           โ”— ๐Ÿ“„ PaymentApiAdapter.java
 โ”‚
 โ”ฃ ๐Ÿ“ config                 // DI, ์„ค์ • ๊ด€๋ จ
 โ”‚  โ”— ๐Ÿ“„ ApplicationConfig.java
 โ”‚
 โ”— ๐Ÿ“„ Application.java       // ๋ฉ”์ธ ์‹คํ–‰ ํŒŒ์ผ (Spring Boot ๋“ฑ)

๐Ÿงฉ ๊ตฌ์กฐ ์„ค๋ช…

  • application
    • ํฌํŠธ(์ธํ„ฐํŽ˜์ด์Šค)์™€ ์„œ๋น„์Šค(์œ ์Šค์ผ€์ด์Šค) ์ •์˜
    • ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ์ง„์ž…์ 
  • domain
    • ์ˆœ์ˆ˜ ๋„๋ฉ”์ธ ๋ชจ๋ธ (Entity, Value Object, ๋„๋ฉ”์ธ ๋กœ์ง)
    • ์™ธ๋ถ€ ๊ธฐ์ˆ ๊ณผ ๋ฌด๊ด€ → ํ…Œ์ŠคํŠธ์™€ ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด
  • adapter
    • in → ๋“œ๋ผ์ด๋น™ ์–ด๋Œ‘ํ„ฐ (Controller, CLI, Event Listener ๋“ฑ)
    • out → ๋“œ๋ฆฌ๋ธ ์–ด๋Œ‘ํ„ฐ (DB, ์™ธ๋ถ€ API, ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค ๊ตฌํ˜„์ฒด)
  • config
    • Spring DI ์„ค์ •, Bean ๋“ฑ๋ก

๐Ÿค DDD , ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜  ๋‘ ๊ฐœ๋…์˜ ๊ด€๊ณ„

  • ๐Ÿงฉ DDD (Domain-Driven Design) = “๋ฌด์—‡์„ ์ค‘์‹ฌ์œผ๋กœ ๊ฐœ๋ฐœํ• ๊นŒ?” → ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ
  • ์ •์˜: ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ์˜ ๋ณต์žก์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•๋ก 
    • ํ•ต์‹ฌ ๋ชฉํ‘œ: ๊ธฐ์ˆ ์ด ์•„๋‹ˆ๋ผ **๋„๋ฉ”์ธ ๋กœ์ง(๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™)**์„ ์ค‘์‹ฌ์— ๋‘๊ณ  ๊ฐœ๋ฐœ
    • 3๊ณ„์ธต ๊ตฌ์กฐ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒŒ ์ผ๋ฐ˜์ :
      1. Application Layer
        • ๋„๋ฉ”์ธ๊ณผ ์ €์žฅ์†Œ๋ฅผ ํ™œ์šฉํ•ด ์œ ์Šค์ผ€์ด์Šค ์‹คํ–‰ (์„œ๋น„์Šค, API ์ œ๊ณต)
      2. Domain Model Layer
        • ์—”ํ‹ฐํ‹ฐ/๋ฐธ๋ฅ˜ ๊ฐ์ฒด/์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ํ†ตํ•ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ˆ˜ํ–‰
      3. Infrastructure Layer
        • DB, ๋ฉ”์‹œ์ง€ ํ, ์™ธ๋ถ€ API ๋“ฑ ์™ธ๋ถ€์™€์˜ ์—ฐ๊ฒฐ ๋‹ด๋‹น
  • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ (Hexagonal Architecture, Ports & Adapters)  = “๊ทธ ๋„๋ฉ”์ธ์„ ์–ด๋–ป๊ฒŒ ์ง€์ผœ์ค„๊นŒ?” → ์•„ํ‚คํ…์ฒ˜์  ๊ตฌ์กฐ

 

  • ์ •์˜: ํฌํŠธ(์ธํ„ฐํŽ˜์ด์Šค)์™€ ์–ด๋Œ‘ํ„ฐ(๊ตฌํ˜„์ฒด) ๊ฐœ๋…์„ ํ™œ์šฉํ•ด
    ๋„๋ฉ”์ธ ๋กœ์ง์„ ์™ธ๋ถ€ ๊ธฐ์ˆ (DB, UI, ํ”„๋ ˆ์ž„์›Œํฌ)๋กœ๋ถ€ํ„ฐ ๊ฒฉ๋ฆฌํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด
  • ํ•ต์‹ฌ ๋ชฉํ‘œ:
    • ๋„๋ฉ”์ธ ๋กœ์ง์ด ์™ธ๋ถ€ ๊ธฐ์ˆ ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋„๋ก ๋ณดํ˜ธ
    • ๊ธฐ์ˆ  ์š”์†Œ๋Š” ์‰ฝ๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅ (DB → Redis, REST → gRPC ๋“ฑ)

 

  • ์ •๋ฆฌ
    1. DDD๋Š” ๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ์ฝ”๋“œ๋กœ ์˜ฎ๊ธฐ๋Š” ๋ฐฉ๋ฒ•๋ก 
      → ๋„๋ฉ”์ธ ์ค‘์‹ฌ ์„ค๊ณ„
    2. ํ—ฅ์‚ฌ๊ณ ๋‚ ์€ ๊ทธ ์„ค๊ณ„๋ฅผ ๊ธฐ์ˆ ๋กœ๋ถ€ํ„ฐ ์ง€์ผœ์ฃผ๋Š” ์•„ํ‚คํ…์ฒ˜
      → ํฌํŠธ์™€ ์–ด๋Œ‘ํ„ฐ๋กœ ๊ฒฐํ•ฉ๋„ ์ตœ์†Œํ™”
    3. ๋‘ ๊ฐœ๋…์€ ์„œ๋กœ ๋ณด์™„์ 
      • DDD๋กœ ๋ชจ๋ธ๋ง → ํ—ฅ์‚ฌ๊ณ ๋‚ ๋กœ ๊ตฌ์กฐํ™” → ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ ํ–ฅ์ƒ

 

๐Ÿ“Š  ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜(Hexagonal, Ports & Adapters)์™€ ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜(Clean Architecture)

๊ตฌ๋ถ„ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜

์ œ์•ˆ์ž Alistair Cockburn Robert C. Martin
๊ตฌ์กฐ ์ฝ”์–ด + ํฌํŠธ + ์–ด๋Œ‘ํ„ฐ ๋™์‹ฌ์› ๊ณ„์ธต ๊ตฌ์กฐ
ํ•ต์‹ฌ ๊ฐœ๋… ํฌํŠธ์™€ ์–ด๋Œ‘ํ„ฐ ์—”ํ‹ฐํ‹ฐ, ์œ ์Šค์ผ€์ด์Šค, ์ธํ„ฐํŽ˜์ด์Šค ์–ด๋Œ‘ํ„ฐ
๋ชฉํ‘œ ๊ธฐ์ˆ  ๋…๋ฆฝ์„ฑ, ์œ ์—ฐํ•œ ํ™•์žฅ ์ฑ…์ž„ ๋ถ„๋ฆฌ, ์˜์กด์„ฑ ๊ทœ์น™ ๊ฐ•ํ™”
๋ณต์žก๋„ ์ƒ๋Œ€์ ์œผ๋กœ ๋‹จ์ˆœ ์ƒ๋Œ€์ ์œผ๋กœ ์„ธ๋ฐ€ํ•˜๊ณ  ๋ณต์žก
์ ์šฉ ์˜ˆ์‹œ DB ๊ต์ฒด, API/๋ฉ”์‹œ์ง• ๊ต์ฒด ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ, ๊ณ„์ธต ๋ถ„๋ฆฌ ์ฒ ์ €ํžˆ ํ•„์š”ํ•  ๋•Œ
  • ํ—ฅ์‚ฌ๊ณ ๋‚ ์€ ๋น„๊ต์  ๋‹จ์ˆœํ•ด์„œ ๊ธฐ์ˆ  ๋…๋ฆฝ์„ฑ์„ ํ™•๋ณดํ•˜๋Š” ๋ฐ ๊ฐ•์ 
  • ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜๋Š” ๊ณ„์ธต์„ ์„ธ๋ฐ€ํžˆ ๋‚˜๋ˆ  ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์—์„œ ์œ ์ง€๋ณด์ˆ˜์— ๊ฐ•์ 
  • ๋‘˜ ๋‹ค “๋„๋ฉ”์ธ ๋กœ์ง์„ ๋ณดํ˜ธํ•œ๋‹ค”๋Š” ๊ณตํ†ต ๋ชฉ์ ์„ ๊ฐ–๊ณ  ์žˆ์Œ
  • ์‹ค์ œ ํ˜„์žฅ์—์„œ๋Š” ํ—ฅ์‚ฌ๊ณ ๋‚ ์„ ๋จผ์ € ์ ์šฉํ•˜๊ณ , ์‹œ์Šคํ…œ์ด ์ปค์ง€๋ฉด ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜๋กœ ํ™•์žฅํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ