Port & Adapter Architecture - 3sam5oh/webtoon-recommendation-service GitHub Wiki

๐Ÿ–ฅ๏ธ Port & Adapter Architecture

AWS Hexagonal Architecture ๋ฌธ์„œ

์šฐํ…Œ์ฝ” ์ฐธ๊ณ  ๋งํฌ

Clean Architecture ๋งํฌ

โฌก Hexagonal Architecture Pattern ?

๊ฐœ์š” & ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ

ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด(Ports and Adapters)์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฃผ์š” ๋กœ์ง์„ ์™ธ๋ถ€ ์š”์†Œ(์˜ˆ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค)์™€ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐœ๋ฐœ์˜ ์œ ์—ฐ์„ฑ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ.

2005๋…„ Alistair Cockburn์˜ํ•ด ์ œ์•ˆ๋œ ์•„ํ‚คํ…์ฒ˜์ด๋ฉฐ, ์ „ํ†ต์ ์ธ ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ฒ˜(Layered Architecture)์˜ ๋ฌธ์ œ์ ์„ ๋ณด์™„ํ•˜๊ณ ์ž ๋“ฑ์žฅํ•˜์˜€์Œ. ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜๋Š” ๋ฐ์ดํ„ฐ ์—‘์„ธ์Šค ๊ณ„์ธต์— ์˜์กดํ•˜๋Š” ๊ตฌ์กฐ์ธ๋ฐ, ์ด๋Š” db๊ฐ™์€ ํ•˜์œ„ ๊ณ„์ธต์ด ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด db์˜ ๋ณ€๊ฒฝ์ด๋‚˜ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์ƒํ˜ธ์ž‘์ด ํ•„์š”ํ•  ๋•Œ ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์‘ํ•˜๊ธฐ ์–ด๋ ค์šด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ, ๋˜ํ•œ ํŠน์ • ๊ธฐ์ˆ ์— ์˜์กด์ ๋Š” ์ฝ”๋“œ๋กœ ์ž‘์„ฑ๋˜๊ณ  ๋„๋ฉ”์ธ ๋กœ์ง๊ณผ ์„ž์ด๋ฉด์„œ ๋กœ์ง์„ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๊ฒƒ์—๋„ ์‰ฝ์ง€ ์•Š๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ.

๋”ฐ๋ผ์„œ ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค์˜ ๋กœ์ง์„ ์™ธ๋ถ€ ๊ธฐ์ˆ  ์Šคํƒ์— ์˜์กดํ•˜์ง€ ์•Š๊ฒŒ ํ•จ์œผ๋กœ์จ, ๋กœ์ง์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ ๋†’์ด๊ณ , ์˜์กด์„ฑ ์—ญ์ „ ์›์น™ (Dependency Inversion Principle)์„ ํ†ตํ•ด, ์™ธ๋ถ€ ํŠน์ • ๊ตฌํ˜„์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•˜๋„๋ก ์„ค๊ณ„ (์ƒ์œ„ ๋ชจ๋“ˆ (High-level modules) ์€ ํ•˜์œ„ ๋ชจ๋“ˆ (Low-level modules) ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋˜๋ฉฐ, ๋‘˜ ๋‹ค ์ถ”์ƒํ™” (abstractions) ์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค).

image

ํ•ต์‹ฌ ๊ฐœ๋…

  1. Domain (๋„๋ฉ”์ธ): ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๋‹น. ๋„๋ฉ”์ธ ๋กœ์ง์€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ์ด๋‚˜ ํ”„๋ ˆ์ž„์›Œํฌ์— ์˜์กดํ•˜์ง€ ์•Š์Œ.

  2. Ports (ํฌํŠธ): ๋„๋ฉ”์ธ ๋กœ์ง๊ณผ ์™ธ๋ถ€ ์‹œ์Šคํ…œ ๊ฐ„์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜. ํฌํŠธ๋Š” ์ž…๋ ฅ(์ž…๋ ฅ ํฌํŠธ)๊ณผ ์ถœ๋ ฅ(์ถœ๋ ฅ ํฌํŠธ)์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Œ.

  3. Adapters (์–ด๋Œ‘ํ„ฐ): ํฌํŠธ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ๋„๋ฉ”์ธ ๋กœ์ง์„ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ฒฐ. ์–ด๋Œ‘ํ„ฐ๋Š” ์™ธ๋ถ€ ์‹œ์Šคํ…œ์ด ๋„๋ฉ”์ธ ๋กœ์ง๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ์ •์˜.

์•„ํ‚คํ…์ฒ˜์˜ ๊ตฌ์กฐ

  • ๋„๋ฉ”์ธ ์ค‘์‹ฌ: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋„๋ฉ”์ธ ๊ณ„์ธต์— ์œ„์น˜ํ•˜๋ฉฐ, ์ด ๊ณ„์ธต์€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์—์„œ ๋…๋ฆฝ์ .
  • ํฌํŠธ์™€ ์–ด๋Œ‘ํ„ฐ ๊ณ„์ธต: ํฌํŠธ๋Š” ๋„๋ฉ”์ธ ๋กœ์ง์ด ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์„ ์ •์˜, ์–ด๋Œ‘ํ„ฐ๋Š” ์ด๋Ÿฌํ•œ ํฌํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‹ค์ œ ์ƒํ˜ธ์ž‘์šฉ์„ ์ˆ˜ํ–‰.

์ด์ 

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

์‚ฌ์šฉ ์˜ˆ์‹œ

  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜: ์›น UI, API, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ ๋‹ค์–‘ํ•œ ์–ด๋Œ‘ํ„ฐ๋ฅผ ํ†ตํ•ด ๋„๋ฉ”์ธ ๋กœ์ง์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ.
  • ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜: ๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋…๋ฆฝ์ ์ธ ๋„๋ฉ”์ธ ๋กœ์ง์„ ๊ฐ€์ง€๊ณ , ๋‹ค๋ฅธ ์„œ๋น„์Šค๋‚˜ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ํ†ต์‹ ์„ ์–ด๋Œ‘ํ„ฐ๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰.

์ ์šฉ ๋ฐฉ๋ฒ•

  1. ๋„๋ฉ”์ธ ๋กœ์ง์„ ์ •์˜ํ•˜๊ณ , ์ด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค๊ณ„.
  2. ๋„๋ฉ”์ธ ๋กœ์ง๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํฌํŠธ๋ฅผ ์ •์˜.
  3. ํฌํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ฒ˜๋ฆฌ.
  4. ๋„๋ฉ”์ธ ๋กœ์ง์˜ ๋…๋ฆฝ์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ, ํ•„์š”์— ๋”ฐ๋ผ ์–ด๋Œ‘ํ„ฐ๋ฅผ ๊ต์ฒดํ•˜๊ฑฐ๋‚˜ ํ™•์žฅ.

๊ฒฐ๋ก 

์œก๊ฐํ˜• ์•„ํ‚คํ…์ฒ˜๋Š” ๋ณต์žกํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์กฐ๋ฅผ ๋‹จ์ˆœํ™”ํ•˜๊ณ , ์œ ์—ฐ์„ฑ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ๋„์›€. ์ด๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ณ€ํ™”ํ•˜๋Š” ์š”๊ตฌ ์‚ฌํ•ญ์— ๋” ์‰ฝ๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Œ.

๐Ÿ“ Directory Structure

Note

์‹ค์ œ ์ ์šฉ ์‚ฌ๋ก€ ๋ฐ ๊ฐœ์„  ๊ณผ์ •์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค

Port & Adapter Architecture์˜ domain

  • ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜์—์„œ์˜ ๋„๋ฉ”์ธ์€ ์ „ํ†ต์ ์ธ mvc ์•„ํ‚คํ…์ฒ˜์˜ ๋„๋ฉ”์ธ์˜ ๊ฐœ๋…๊ณผ๋Š” ๋‹ค๋ฅด๋‹ค.
  • ์ „ํ†ต์ ์ธ mvc ์•„ํ‚คํ…์ฒ˜ ๋„๋ฉ”์ธ์˜ ์—”ํ‹ฐํ‹ฐ๋Š” db์™€ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•˜๋Š” ์‹ค์ œ ํ•„๋“œ๊ฐ’์„ ์˜๋ฏธํ•˜๋ฉฐ ORM์ด ์ ์šฉ๋œ๋‹ค.
  • ํ•˜์ง€๋งŒ ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜์˜ ๋„๋ฉ”์ธ์€ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ ์ผ์–ด๋‚˜์•ผ ํ•˜๋Š” action์„ ์ •์˜ํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜์˜ domain(model)์— ์ƒ์„ฑ๋˜๋Š” ์—”ํ‹ฐํ‹ฐ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐ๋˜๋Š” ๋งตํ•‘ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹Œ ๊ตฌํ˜„ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋œ๋‹ค.
  • ๊ฒ‰์œผ๋กœ ๋ณด๊ธฐ์—๋Š” ๋˜‘๊ฐ™์ด ํ•„๋“œ๊ฐ’์„ ๋งŒ๋“ค๊ณ  ๋นŒ๋” ํŒจํ„ด์„ ํ™œ์šฉํ•˜์—ฌ ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ค์ง€๋งŒ ์ด๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” action์„ ์ •์˜ ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค
  • ๋”ฐ๋ผ์„œ application ์„œ๋น„์Šค์—์„œ๋Š” ํ•ด๋‹น ์•ก์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ƒ์„ฑ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์›นํˆฐ ์ถ”์ฒœ์„ ์œ„ํ•œ ๊ฒ€์ƒ‰์ด ์„œ๋น„์Šค์—์„œ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด, ๋„๋ฉ”์ธ์—์„œ๋Š” ์ถ”์ฒœ action (์‹ค์ œ๋กœ๋Š” method๋ฅผ ์ž‘์„ฑ), ๊ฒ€์ƒ‰ action์„ ๋งŒ๋“ค๊ณ  ์„œ๋น„์Šค์—์„œ๋Š” ํ•ด๋‹น action์„ ์กฐํ•ฉํ•˜์—ฌ ์ถ”์ฒœ ๊ฒ€์ƒ‰์ด๋ผ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•œ๋‹ค.
  • ๋ฌผ๋ก  ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜์˜ domain์—๋„ orm์„ ํ†ตํ•œ db์™€ ํ†ต์‹ ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ ํŠน์ • ์˜์†์„ฑ ๋ ˆ์ด์–ด์— ์ข…์†์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.

Port & Adapter Architecture ๋„์ž… ์ „ ๊ณ ๋ ค ์‚ฌํ•ญ

  • ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜๋Š” ๊ธฐ์กด์˜ mvc ์•„ํ‚คํ…์ฒ˜์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๋Œ€๋ถ€๋ถ„์˜ ํ†ต์‹ ์„ ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด์„œ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ํ™”๋ฉด๊ณผ db์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ณ  ์„œ๋น„์Šค ๋กœ์ง์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ์ถ”์ƒํ™”๋Š” ๊ฐ€๋…์„ฑ์ด ๋‚ฎ์•„์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์œผ๋ฉฐ, ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ๊ฐ„๋‹จํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ์œ ์ง€ ๋ณด์ˆ˜๋ฅผ ํ•˜๊ธฐ ์œ„ํ•œ ์ธ์›์ด ์ž์ฃผ ๊ต์ฒด ๋˜๋Š” ์„œ๋น„์Šค์—๋Š” ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋„์ž… ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ํ›„์ž„์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋งŽ์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค(Complexity๊ฐ€ ๋†’๊ธฐ ๋•Œ๋ฌธ์— ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒ ํ–ˆ์„ ๋•Œ ํ•ด๊ฒฐ์ด ๋ณต์žกํ•  ์ˆ˜ ์žˆ๋‹ค).
  • ๋”ฐ๋ผ์„œ ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋„์ž…ํ•˜๊ธฐ ์ „์—๋Š” ์„œ๋น„์Šค ๋กœ์ง์ด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š”์ง€, ์œ ์ง€ ๋ณด์ˆ˜๋ฅผ ์œ„ํ•œ ์ธ์›์ด ์ž์ฃผ ๊ต์ฒด๋˜๋Š”์ง€, ๊ฐ„๋‹จํ•œ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ๊ทธ๋žจ์ธ์ง€๋ฅผ ๊ณ ๋ฏผํ•˜๊ณ  ๋„์ž… ํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.
  • port & adapter ์•„ํ‚คํ…์ฒ˜๋Š” ์ถ”๊ฐ€์ ์ธ ๋ ˆ์ด์–ด๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— latency issue๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค (์ด๊ฑด ์–ด๋–ป๊ฒŒ ์ธก์ •ํ•˜์ง€?)
  • ์—ฌ๋Ÿฌ ํ™”๋ฉด๊ณผ db ์—ฐ๊ฒฐ์„ ๊ณ ๋ คํ•œ๋‹ค๋ฉด ํ—ฅ์‚ฌ๊ณ ๋„ ์•„ํ‚คํ…์ฒ˜๋Š” ์ข‹์€ ์„ ํƒ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์˜ˆ์‹œ

src/
โ””โ”€โ”€ main/
    โ””โ”€โ”€ java/
        โ””โ”€โ”€ org/
            โ””โ”€โ”€ samsamohoh/
                โ””โ”€โ”€ webtoonrecommendation/
                    โ”œโ”€โ”€ application/
                    โ”‚   โ”œโ”€โ”€ model/
                    โ”‚   โ”‚   โ”œโ”€โ”€ webtoon/
                    โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Webtoon.java
                    โ”‚   โ”‚   โ””โ”€โ”€ user/
                    โ”‚   โ”‚       โ””โ”€โ”€ User.java
                    โ”‚   โ”œโ”€โ”€ service/
                    โ”‚   โ”‚   โ”œโ”€โ”€ webtoon/
                    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ WebtoonService.java
                    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ WebtoonRecommendationService.java
                    โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ WebtoonSearchService.java
                    โ”‚   โ”‚   โ””โ”€โ”€ user/
                    โ”‚   โ”‚       โ””โ”€โ”€ UserService.java
                    โ”‚   โ””โ”€โ”€ port/
                    โ”‚       โ”œโ”€โ”€ in/
                    โ”‚       โ”‚   โ”œโ”€โ”€ webtoon/
                    โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ RecommendWebtoonUseCase.java
                    โ”‚       โ”‚   โ”‚   โ””โ”€โ”€ SearchWebtoonUseCase.java
                    โ”‚       โ”‚   โ””โ”€โ”€ user/
                    โ”‚       โ”‚       โ””โ”€โ”€ ManageUserUseCase.java
                    โ”‚       โ””โ”€โ”€ out/
                    โ”‚           โ”œโ”€โ”€ webtoon/
                    โ”‚           โ”‚   โ”œโ”€โ”€ WebtoonPersistencePort.java
                    โ”‚           โ”‚   โ””โ”€โ”€ WebtoonSearchPort.java
                    โ”‚           โ””โ”€โ”€ user/
                    โ”‚               โ””โ”€โ”€ UserPersistencePort.java
                    โ””โ”€โ”€ adapter/
                        โ”œโ”€โ”€ in/
                        โ”‚   โ””โ”€โ”€ rest/
                        โ”‚       โ”œโ”€โ”€ dto/
                        โ”‚       โ”‚   โ”œโ”€โ”€ WebtoonDTO.java
                        โ”‚       โ”‚   โ””โ”€โ”€ UserDTO.java
                        โ”‚       โ”œโ”€โ”€ WebtoonController.java
                        โ”‚       โ””โ”€โ”€ UserController.java
                        โ””โ”€โ”€ out/
                            โ”œโ”€โ”€ persistence/
                            โ”‚   โ”œโ”€โ”€ webtoon/
                            โ”‚   โ”‚   โ””โ”€โ”€ JpaWebtoonPersistenceAdapter.java
                            โ”‚   โ””โ”€โ”€ user/
                            โ”‚       โ””โ”€โ”€ JpaUserPersistenceAdapter.java
                            โ””โ”€โ”€ search/
                                โ””โ”€โ”€ OpenSearchWebtoonAdapter.java

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ๊ฐœ์„ ?

  • Application์ด ์–ด๋–ค Action์„ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ธ๊ฐ€์— ๋Œ€ํ•œ ์ •์˜์ด๋ฉฐ, Application ๊ด€์ ์—์„œ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์š”์ฒญ ์‚ฌ๋ก€(Inbound UseCase) ๊ทธ๋ฆฌ๊ณ  ์™ธ๋ถ€์˜ ์š”์ฒญ์ด ํ•„์š”ํ•œ ์‚ฌ๋ก€(Outbound Request)๋กœ ๋‚˜๋‰˜์–ด์„œ ์ •์˜๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด ์‹ค์ œ application port์˜ ์—ญํ• .
  • Application์—์„œ ์–ด๋–ค Action์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” Input, Output์„ ์–ด๋–ป๊ฒŒ ๋ฐ›์„์ง€ "๋ฉ”์‹œ์ง€"๊ฐ€ ์ •์˜๋˜์–ด์•ผ ํ•œ๋‹ค.
  • Adapter์˜ port๋Š” Application์˜ Port์˜ ์‹ค์ œ ๊ตฌํ˜„์ฒด ์—ญํ• ์„ ๋‹ด๋‹น. ๋”ฐ๋ผ์„œ Adapter์˜ port๋Š” ๋ณธ์ธ์ด In Adapter์ธ์ง€ Out Adapter์ธ์ง€ ์•Œ ํ•„์š”๋„ ์•Œ ์ˆ˜๋„ ์—†๊ฒŒ ๊ตฌํ˜„. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ Inbound, Outbount๋กœ ์“ฐ์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ.
  • ํ˜„์žฌ๋Š” ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š์ง€๋งŒ Port & Adapter ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, gradle multi module์ด ์ถ”์ฒœ. single module๋กœ๋งŒ ๊ตฌํ˜„๋˜๋ฉด ์ถ”ํ›„์— ์ฝ”๋“œ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•˜์—ฌ ํšจ์œจ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์Œ.

๊ฐœ์„ ๋œ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์˜ˆ์‹œ

com.example.project
โ”œโ”€โ”€ ProjectApplication.java            // ๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค
โ”œโ”€โ”€ adapter                            // ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋‹ด๋‹น
โ”‚   โ”œโ”€โ”€ config                         // ์„ค์ • ๊ด€๋ จ ํด๋ž˜์Šค๋“ค
โ”‚   โ”‚   โ”œโ”€โ”€ AppConfig.java             // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •
โ”‚   โ”‚   โ”œโ”€โ”€ SecurityConfig.java        // ๋ณด์•ˆ ์„ค์ •
โ”‚   โ”œโ”€โ”€ persistence                    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋‹ด๋‹น
โ”‚   โ”‚   โ”œโ”€โ”€ UserPersistenceAdapter.java  // ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ์†Œ ์–ด๋Œ‘ํ„ฐ
โ”‚   โ”‚   โ”œโ”€โ”€ DataEntity.java            // ๋ฐ์ดํ„ฐ ์—”ํ‹ฐํ‹ฐ
โ”‚   โ”œโ”€โ”€ rdbms                          // JPA ๊ด€๋ จ ์–ด๋Œ‘ํ„ฐ
โ”‚   โ”‚   โ”œโ”€โ”€ UserJpaAdapter.java        // JPA ์‚ฌ์šฉ์ž ์ €์žฅ์†Œ ์–ด๋Œ‘ํ„ฐ
โ”‚   โ”œโ”€โ”€ searchengine                   // ๊ฒ€์ƒ‰ ์—”์ง„๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋‹ด๋‹น
โ”‚   โ”‚   โ”œโ”€โ”€ SearchEngineAdapter.java   // ๊ฒ€์ƒ‰ ์—”์ง„ ์–ด๋Œ‘ํ„ฐ
โ”‚   โ”œโ”€โ”€ web  // ์›น ๊ณ„์ธต
โ”‚       โ”œโ”€โ”€ user
โ”‚       โ”‚   โ”œโ”€โ”€ UserController.java    // ์‚ฌ์šฉ์ž ์ปจํŠธ๋กค๋Ÿฌ
โ”‚       โ”œโ”€โ”€ content
โ”‚           โ”œโ”€โ”€ ContentController.java // ์ฝ˜ํ…์ธ  ์ปจํŠธ๋กค๋Ÿฌ
โ”‚           โ”œโ”€โ”€ ContentResponse.java   // ์ฝ˜ํ…์ธ  ์‘๋‹ต DTO
โ”œโ”€โ”€ application                        // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ port  // ํฌํŠธ์™€ ๊ด€๋ จ๋œ ์ธํ„ฐํŽ˜์ด์Šค
โ”‚   โ”‚   โ”œโ”€โ”€ in
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ RegisterUserUseCase.java   // ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์œ ์Šค์ผ€์ด์Šค
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ SearchContentUseCase.java  // ์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰ ์œ ์Šค์ผ€์ด์Šค
โ”‚   โ”‚   โ”œโ”€โ”€ out
โ”‚   โ”‚       โ”œโ”€โ”€ AddDataPort.java       // ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ํฌํŠธ
โ”‚   โ”‚       โ”œโ”€โ”€ LoadContentPort.java   // ์ฝ˜ํ…์ธ  ๋กœ๋“œ ํฌํŠธ
โ”‚   โ”œโ”€โ”€ service
โ”‚       โ”œโ”€โ”€ RegisterUserService.java   // ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์„œ๋น„์Šค
โ”‚       โ”œโ”€โ”€ SearchContentService.java  // ์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰ ์„œ๋น„์Šค
โ”œโ”€โ”€ common                             // ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํด๋ž˜์Šค๋“ค
โ”‚   โ”œโ”€โ”€ ApiResponse.java               // API ์‘๋‹ต ๊ณตํ†ต ํด๋ž˜์Šค
โ”‚   โ”œโ”€โ”€ logs
โ”‚   โ”‚   โ”œโ”€โ”€ LoggingAspect.java         // ๋กœ๊น… ์–ด์ŠคํŽ™ํŠธ
โ”‚   โ”œโ”€โ”€ metrics
โ”‚       โ”œโ”€โ”€ MetricsConfig.java         // ๋ฉ”ํŠธ๋ฆญ ์„ค์ •
โ”œโ”€โ”€ domain  // ๋„๋ฉ”์ธ ๋ชจ๋ธ
โ”‚   โ”œโ”€โ”€ UserValidation.java            // ์‚ฌ์šฉ์ž ๊ฒ€์ฆ
โ”‚   โ”œโ”€โ”€ SearchableContent.java         // ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅํ•œ ์ฝ˜ํ…์ธ 

์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

com.samsamohoh.webtoonsearch
โ”œโ”€โ”€ WebtoonSearchApplication.java
โ”œโ”€โ”€ adapter
โ”‚   โ”œโ”€โ”€ config
โ”‚   โ”‚   โ”œโ”€โ”€ SearchEngineConfig.java
โ”‚   โ”‚   โ”œโ”€โ”€ SecurityConfig.java
โ”‚   โ”œโ”€โ”€ persistence
โ”‚   โ”‚   โ”œโ”€โ”€ MemberPersistenceAdapter.java
โ”‚   โ”‚   โ”œโ”€โ”€ RegisterMemberEntity.java
โ”‚   โ”‚   โ”œโ”€โ”€ SearchPersistenceAdapter.java
โ”‚   โ”‚   โ”œโ”€โ”€ SearchWebtoonEntity.java
โ”‚   โ”‚   โ”œโ”€โ”€ SelectPersistenceAdapter.java
โ”‚   โ”‚   โ”œโ”€โ”€ SelectRecordEntity.java
โ”‚   โ”œโ”€โ”€ rdbms
โ”‚   โ”‚   โ”œโ”€โ”€ MemberPersistenceAdapter.java
โ”‚   โ”‚   โ”œโ”€โ”€ SelectRecordAdapter.java
โ”‚   โ”œโ”€โ”€ searchengine
โ”‚   โ”‚   โ”œโ”€โ”€ SearchEngineAdapter.java
โ”‚   โ”œโ”€โ”€ web
โ”‚       โ”œโ”€โ”€ member
โ”‚       โ”‚   โ”œโ”€โ”€ OAuthController.java
โ”‚       โ”‚   โ”œโ”€โ”€ OAuthResponse.java
โ”‚       โ”œโ”€โ”€ webtoon
โ”‚           โ”œโ”€โ”€ SearchWebtoonController.java
โ”‚           โ”œโ”€โ”€ SearchWebtoonResponse.java
โ”‚           โ”œโ”€โ”€ SelectRecordController.java
โ”‚           โ”œโ”€โ”€ SelectRecordRequest.java
โ”œโ”€โ”€ application
โ”‚   โ”œโ”€โ”€ port
โ”‚   โ”‚   โ”œโ”€โ”€ in
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ member
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ RegisterMemberCommand.java
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ RegisterMemberUseCase.java
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ webtoon
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ LoadWebtoonQuery.java
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ SearchWebtoonCommand.java
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ SearchWebtoonUseCase.java
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ SelectRecordUseCase.java
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ SelectWebtoonDTO.java
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ WebtoonResult.java
โ”‚   โ”‚   โ”œโ”€โ”€ out
โ”‚   โ”‚       โ”œโ”€โ”€ AddRecordPort.java
โ”‚   โ”‚       โ”œโ”€โ”€ LoadWebtoonPort.java
โ”‚   โ”‚       โ”œโ”€โ”€ SaveMemberPort.java
โ”‚   โ”œโ”€โ”€ service
โ”‚       โ”œโ”€โ”€ RegisterMemberService.java
โ”‚       โ”œโ”€โ”€ SearchWebtoonService.java
โ”‚       โ”œโ”€โ”€ SelectRecordService.java
โ”œโ”€โ”€ common
โ”‚   โ”œโ”€โ”€ ApiResponse.java
โ”‚   โ”œโ”€โ”€ logs
โ”‚   โ”‚   โ”œโ”€โ”€ LoggingAspect.java
โ”‚   โ”œโ”€โ”€ metrics
โ”‚       โ”œโ”€โ”€ CustomMetrics.java
โ”‚       โ”œโ”€โ”€ config
โ”‚           โ”œโ”€โ”€ MetricsConfig.java
โ”œโ”€โ”€ domain
โ”‚   โ”œโ”€โ”€ MemberValidation.java
โ”‚   โ”œโ”€โ”€ SearchableWebtoon.java

โš ๏ธ **GitHub.com Fallback** โš ๏ธ