Post

[System Design] Modular Monolith

[System Design] Modular Monolith

๐Ÿงฉ Module

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ๋•Œ ํ”ํžˆ ์ €์ง€๋ฅด๋Š” ์‹ค์ˆ˜๋Š” ๋ชจ๋“ˆ์„ ๋‹จ์ˆœํžˆ โ€˜๊ธฐ๋Šฅ๋ณ„๋กœ ๋ถ„๋ฅ˜๋œ ํด๋”โ€™๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—”์ง€๋‹ˆ์–ด๋ง ๊ด€์ ์—์„œ ๋ชจ๋“ˆ์€ ์ถ”์ƒํ™”์˜ ๋ฒฝ(Abstraction Wall)์ž…๋‹ˆ๋‹ค. ๋‚ด๋ถ€์˜ ๋ณต์žก์„ฑ์„ ์™ธ๋ถ€๋กœ ์œ ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด์„œ๋„, ์‹œ์Šคํ…œ์˜ ์ „์ฒด์ ์ธ ์ •ํ•ฉ์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋…ผ๋ฆฌ์  ์š”์ƒˆ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“ˆํ™”๊ฐ€ ์‹คํŒจํ•œ ๋ชจ๋†€๋ฆฌ์Šค๋Š” ๊ฒฐ๊ตญ ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•˜๋Š” โ€˜์ปค๋‹ค๋ž€ ์ง„ํ™ ๋ฉ์–ด๋ฆฌ(Big Ball of Mud)โ€™๋กœ ์ „๋ฝํ•˜๋ฉฐ, ์ด๋Š” ๊ณง ์ˆ˜์ • ํ•˜๋‚˜๊ฐ€ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฐ•์ œํ•˜๋Š” ์žฌ์•™์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

Encapsulation

์ง„์ •ํ•œ ์˜๋ฏธ์˜ ๋ชจ๋“ˆํ™”๋Š” ์บก์Аํ™”์—์„œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ์บก์Аํ™”๋Š” ๋‹จ์ˆœํžˆ ํ•„๋“œ๋ฅผ private์œผ๋กœ ์„ ์–ธํ•˜๋Š” ํ–‰์œ„๋ฅผ ๋„˜์–ด, ๋ชจ๋“ˆ ๋‚ด๋ถ€์˜ ๋ถˆ๋ณ€์„ฑ(Invariants)์„ ๋ณดํ˜ธํ•˜๊ณ  ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ์ˆจ๊ธฐ๋Š” ์ „๋žต์  ์„ ํƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“ˆ ๋‚ด๋ถ€์˜ ๋„๋ฉ”์ธ ๋กœ์ง์ด๋‚˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๊ฐ€ ์™ธ๋ถ€๋กœ ๋…ธ์ถœ๋˜๋Š” ์ˆœ๊ฐ„, ๊ทธ ๋ชจ๋“ˆ์€ ๋” ์ด์ƒ ๋…๋ฆฝ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€์˜ ์ˆ˜๋งŽ์€ ์ฐธ์กฐ์ž๊ฐ€ ํ•ด๋‹น ๊ตฌ์กฐ์— ์˜์กดํ•˜๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ๋ชจ๋“ˆ ๋‚ด๋ถ€๋ฅผ โ€˜Black Boxโ€™๋กœ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€์—์„œ๋Š” ๋ชจ๋“ˆ์ด ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋Š”์ง€, ์–ด๋–ค ๋ณต์žกํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๊ฒฐ๊ณผ๊ฐ’์„ ๋„์ถœํ•˜๋Š”์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์˜ค์ง ์•ฝ์†๋œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์ƒํ˜ธ์ž‘์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์—„๊ฒฉํ•œ ์บก์Аํ™”๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ชจ๋“ˆ ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•  ๋•Œ โ€˜์™ธ๋ถ€์— ๋ฏธ์น  ์˜ํ–ฅโ€™์„ ๊ณ ๋ฏผํ•˜์ง€ ์•Š๊ฒŒ ํ•จ์œผ๋กœ์จ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ๋น„์•ฝ์ ์œผ๋กœ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

Public API

๋ชจ๋“ˆ์˜ Public API๋Š” ํ•ด๋‹น ๋ชจ๋“ˆ์ด ์„ธ์ƒ๊ณผ ์†Œํ†ตํ•˜๋Š” ์œ ์ผํ•œ ์ฐฝ๊ตฌ์ž…๋‹ˆ๋‹ค. ์ด ์ฐฝ๊ตฌ๋Š” ์ตœ๋Œ€ํ•œ ์ข๊ณ  ๊ฒฌ๊ณ ํ•˜๊ฒŒ ์„ค๊ณ„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Java ์ง„์˜์—์„œ๋Š” ํŒจํ‚ค์ง€ ๊ฐ€์‹œ์„ฑ(Package Visibility)์„ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜ Java 9+์˜ Module System์„ ํ†ตํ•ด ๋ช…์‹œ์ ์œผ๋กœ exportsํ•  ๋Œ€์ƒ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Spring Modulith ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ํŒจํ‚ค์ง€(์˜ˆ: .internal) ํ•˜์œ„์˜ ํด๋ž˜์Šค๋“ค์„ ์™ธ๋ถ€ ๋ชจ๋“ˆ์—์„œ ์ฐธ์กฐํ•  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ ๋‹จ๊ณ„์—์„œ ์‹คํŒจ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ฌผ๋ฆฌ์ ์ธ ๊ฒฉ๋ฆฌ๋ฅผ ๊ฐ•์ œํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ข‹์€ API ์„ค๊ณ„๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๊ณ , ์˜ค์šฉํ•˜๊ธฐ ์–ด๋ ค์›Œ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋ชจ๋“ˆ ๊ฐ„ ํ†ต์‹ ์—๋Š” ๋‚ด๋ถ€ ์—”ํ‹ฐํ‹ฐ(Entity)๋ฅผ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœํ•˜์ง€ ์•Š๊ณ , ์˜ค์ง ํ†ต์‹ ์„ ์œ„ํ•œ ์ˆœ์ˆ˜ํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์ธ DTO(Data Transfer Object)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์›์น™์ž…๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ์ด API ์ŠคํŽ™์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ „์ด๋˜๋Š” ๊ฒƒ์„ ์ฐจ๋‹จํ•˜๋Š” ๋ฐฉํ™”๋ฒฝ ์—ญํ• ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. ์ฃผ๋ฌธ ๋ชจ๋“ˆ์˜ ๊ณต๊ฐœ API (์™ธ๋ถ€ ๋ชจ๋“ˆ์—์„œ ์ฐธ์กฐ ๊ฐ€๋Šฅ)
package com.example.order;

import lombok.Value;

public interface OrderService {
    void placeOrder(OrderRequest request);
}

@Value
public class OrderRequest {
    String productId;
    int quantity;
}

// 2. ์ฃผ๋ฌธ ๋ชจ๋“ˆ์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„ (์™ธ๋ถ€ ๋ชจ๋“ˆ์—์„œ ์ฐธ์กฐ ๋ถˆ๊ฐ€ - ์บก์Аํ™”)
package com.example.order.internal;

import com.example.order.OrderRequest;
import org.springframework.stereotype.Service;

@Service
class OrderProcessor { // package-private์œผ๋กœ ์„ ์–ธํ•˜์—ฌ ์™ธ๋ถ€ ์ ‘๊ทผ ์ฐจ๋‹จ
    public void validate(OrderRequest request) {
        // ๋ณต์žกํ•œ ๋‚ด๋ถ€ ๊ฒ€์ฆ ๋กœ์ง
    }
}

Domain Autonomy

๊ฐ ๋ชจ๋“ˆ์€ ์ž์‹ ์˜ ๋„๋ฉ”์ธ ์˜์—ญ์— ๋Œ€ํ•ด ์ ˆ๋Œ€์ ์ธ ์ž์œจ์„ฑ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“ˆ์ด ์Šค์Šค๋กœ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ์ž์‹ ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ์™„๊ฒฐ์„ฑ ์žˆ๊ฒŒ ์ฒ˜๋ฆฌํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด โ€˜์ฃผ๋ฌธ(Order)โ€™ ๋ชจ๋“ˆ์€ โ€˜์žฌ๊ณ (Inventory)โ€™ ๋ชจ๋“ˆ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ์ง์ ‘ ์กฐํšŒํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์žฌ๊ณ  ํ™•์ธ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์žฌ๊ณ  ๋ชจ๋“ˆ์ด ์ œ๊ณตํ•˜๋Š” API๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜, ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ํ†ต์ง€๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ž์œจ์„ฑ์€ ํŒ€ ๋‹จ์œ„์˜ ๋ณ‘๋ ฌ ๊ฐœ๋ฐœ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ํ•ต์‹ฌ ๋™๋ ฅ์ž…๋‹ˆ๋‹ค. ๊ฐ ํŒ€์€ ์ž์‹ ์ด ๋งก์€ ๋ชจ๋“ˆ์˜ ๊ฒฝ๊ณ„ ์•ˆ์—์„œ ์ž์œ ๋กญ๊ฒŒ ๊ธฐ์ˆ  ์Šคํƒ์„ ์ตœ์ ํ™”ํ•˜๊ฑฐ๋‚˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“ˆ์ด ์ž์œจ์ ์ผ์ˆ˜๋ก, ์‹œ์Šคํ…œ์€ ๋งˆ์น˜ ๋ ˆ๊ณ  ๋ธ”๋ก์ฒ˜๋Ÿผ ์œ ์—ฐํ•˜๊ฒŒ ์กฐ๋ฆฝ๋˜๊ณ  ํ™•์žฅ๋  ์ˆ˜ ์žˆ๋Š” ์œ ๊ธฐ์ฒด๋กœ ๊ฑฐ๋“ญ๋‚ฉ๋‹ˆ๋‹ค.

๋ชจ๋“ˆํ™”์˜ ๋น„์šฉ๊ณผ ๊ฐ€์น˜

๋ชจ๋“  ์ฝ”๋“œ ๋ญ‰์น˜๋ฅผ ๋ชจ๋“ˆ๋กœ ์ชผ๊ฐœ๋Š” ๊ฒƒ์€ ๊ณผ์ž‰ ์—”์ง€๋‹ˆ์–ด๋ง์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“ˆํ™”๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„์™€ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์ด๋ผ๋Š” ์ถ”๊ฐ€์ ์ธ ๋น„์šฉ์„ ์ˆ˜๋ฐ˜ํ•ฉ๋‹ˆ๋‹ค.

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

๐Ÿงฉ Monolith

๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜์—์„œ Monolith๋Š” ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌ๋œ ๋ชจ๋“ˆ๋“ค์ด โ€˜๋ฌผ๋ฆฌ์ ์œผ๋กœ๋Š” ํ•˜๋‚˜๋กœ ํ†ตํ•ฉ๋˜์–ด ๋ฐฐํฌ๋จโ€™์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜(MSA)๊ฐ€ ๊ฐ•์ œํ•˜๋Š” ๋„คํŠธ์›Œํฌ ๊ฒฝ๊ณ„(Network Boundary) ๋Œ€์‹ , ํ”„๋กœ์„ธ์Šค ๋‚ด ํ†ต์‹ (In-process Communication)์„ ์„ ํƒํ•จ์œผ๋กœ์จ ์šด์˜ ๋ณต์žก๋„๋ฅผ ์ œ์–ดํ•˜๋Š” ์ „๋žต์  ๊ฒฐ์ •์ž…๋‹ˆ๋‹ค. ๋ชจ๋“ˆํ™”๋œ ์ฝ”๋“œ๊ฐ€ ํ•˜๋‚˜์˜ ์‹คํ–‰ ๋‹จ์œ„๋กœ ๋ฌถ์ผ ๋•Œ, ์‹œ์Šคํ…œ์€ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์˜ ๊ณ ์งˆ์ ์ธ ๋ฌธ์ œ์ธ ๋„คํŠธ์›Œํฌ ์ง€์—ฐ(Latency)๊ณผ ๋ถ€๋ถ„ ์‹คํŒจ(Partial Failure)์˜ ๊ณตํฌ์—์„œ ํ•ด๋ฐฉ๋ฉ๋‹ˆ๋‹ค.

Deployment Unit

๋ชจ๋…ธ๋ฆฌ์Šค์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ๋Š” ๋‹จ์ผ ๋ฐฐํฌ ์œ ๋‹›(Single Deployment Unit)์ด ์ฃผ๋Š” ๋‹จ์ˆœํ•จ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๋„๋ฉ”์ธ ๋กœ์ง์ด ํ•˜๋‚˜์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜น์€ ์•„ํ‹ฐํŒฉํŠธ๋กœ ๋นŒ๋“œ๋˜์–ด ๋ฐฐํฌ๋˜๋ฏ€๋กœ, ์ง€์†์  ํ†ตํ•ฉ ๋ฐ ๋ฐฐํฌ(CI/CD) ํŒŒ์ดํ”„๋ผ์ธ์ด ๊ทน๋„๋กœ ๊ฐ„๊ฒฐํ•ด์ง‘๋‹ˆ๋‹ค. ์„œ๋น„์Šค ๊ฐ„ ๋ฒ„์ „ ๋ถˆ์ผ์น˜๋กœ ์ธํ•œ โ€˜์˜์กด์„ฑ ์ง€์˜ฅโ€™์ด๋‚˜ ๋ณต์žกํ•œ ์„œ๋น„์Šค ๋ฉ”์‹œ(Service Mesh) ์„ค์ • ์—†์ด๋„ ์‹œ์Šคํ…œ์„ ์•ˆ์ •์ ์œผ๋กœ ์šด์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, ๋ชจ๋“ˆ ๊ฐ„ ํ˜ธ์ถœ์ด ์ธ๋ฉ”๋ชจ๋ฆฌ ํ•จ์ˆ˜ ํ˜ธ์ถœ(In-memory call)๋กœ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”์— ๋”ฐ๋ฅธ CPU ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ณ ์„ฑ๋Šฅ์ด ์š”๊ตฌ๋˜๋Š” ๋ฐ์ดํ„ฐ ์ง‘์•ฝ์  ์ž‘์—…์—์„œ ์••๋„์ ์ธ ํšจ์œจ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์˜ ๋ณต์žก์„ฑ์„ ๊ณ ๋ฏผํ•˜๋Š” ๋Œ€์‹ , ๋‹จ์ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ๋ชจ๋“ˆ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Resource Contention

๋‹จ์ผ ํ”„๋กœ์„ธ์Šค ๊ตฌ์กฐ๋Š” ํšจ์œจ์ ์ด์ง€๋งŒ, ์ž์› ๊ฒฝํ•ฉ(Resource Contention)์ด๋ผ๋Š” ์น˜๋ช…์ ์ธ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ์ˆ˜๋ฐ˜ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ชจ๋“ˆ์ด ๋™์ผํ•œ ํž™ ๋ฉ”๋ชจ๋ฆฌ(Heap Memory)์™€ CPU ์ฝ”์–ด๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํŠน์ • ๋ชจ๋“ˆ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋‚˜ ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ์ด ์‹œ์Šคํ…œ ์ „์ฒด์˜ ๊ฐ€์šฉ์„ฑ์„ ์œ„ํ˜‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ โ€˜ํญ๋ฐœ ๋ฐ˜๊ฒฝ(Blast Radius)โ€™ ๋ฌธ์ œ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์˜€๋‹ค๋ฉด ํŠน์ • ์„œ๋น„์Šค๋งŒ ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ๋๋‚  ์ผ์ด, ๋ชจ๋…ธ๋ฆฌ์Šค์—์„œ๋Š” ๋‹จ ํ•˜๋‚˜์˜ ์ž˜๋ชป๋œ ๋ชจ๋“ˆ๋กœ ์ธํ•ด ์ „์ฒด ์‹œ์Šคํ…œ์ด ๋‹ค์šด๋˜๋Š” ์—ฐ์‡„ ๋ฐ˜์‘์„ ์ผ์œผํ‚ต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค๋ฅผ ์ง€ํƒฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ชจ๋“ˆ๋ณ„ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์— ๋Œ€ํ•œ ์ •๋ฐ€ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ, ํŠน์ • ๋ชจ๋“ˆ์ด ์ „์ฒด ์Šค๋ ˆ๋“œ ํ’€์„ ์ ์œ ํ•˜์ง€ ๋ชปํ•˜๋„๋ก ์ฐจ๋‹จํ•˜๋Š” ์„œํ‚ท ๋ธŒ๋ ˆ์ด์ปค(Circuit Breaker) ํ˜น์€ ๋ฒŒํฌํ—ค๋“œ(Bulkhead) ํŒจํ„ด์˜ ์ ์šฉ์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

๋‹จ์ผ์„ฑ์ธ๊ฐ€, ๊ฒฉ๋ฆฌ์ธ๊ฐ€?

๋ชจ๋…ธ๋ฆฌ์Šค๋ฅผ ์„ ํƒํ•œ๋‹ค๋Š” ๊ฒƒ์€ โ€˜์šด์˜์˜ ํŽธ์˜์„ฑโ€™์„ ์œ„ํ•ด โ€˜๋ฌผ๋ฆฌ์  ๊ฒฉ๋ฆฌโ€™๋ฅผ ํฌ๊ธฐํ•˜๋Š” ์„ ํƒ์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ ํŠน์ • ๋ชจ๋“ˆ์˜ ํŠธ๋ž˜ํ”ฝ์ด ๋‹ค๋ฅธ ๋ชจ๋“ˆ๋ณด๋‹ค ์ˆ˜๋งŒ ๋ฐฐ ์ด์ƒ ๋†’๊ฑฐ๋‚˜, ๋…์ž์ ์ธ ์Šค์ผ€์ผ๋ง ์ •์ฑ…์ด ์ ˆ์‹คํ•˜๋‹ค๋ฉด ๊ทธ ์ง€์ ์ด ๋ฐ”๋กœ ๋ชจ๋…ธ๋ฆฌ์Šค์˜ ๊ฒฝ๊ณ„๊ฐ€ ๋ฌด๋„ˆ์ง€๊ณ  MSA๋กœ์˜ ์ „ํ™˜์„ ๊ณ ๋ คํ•ด์•ผ ํ•  ์‹œ์ ์ž…๋‹ˆ๋‹ค.

๐Ÿงฉ Boundary

๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค์˜ ์„ฑํŒจ๋Š” ์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ๋‚˜๋ˆ„๋А๋ƒ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋‚˜๋‰œ ์ฝ”๋“œ ์‚ฌ์ด์— ์–ผ๋งˆ๋‚˜ ๊ฒฌ๊ณ ํ•œ ๊ฒฝ๊ณ„(Boundary)๋ฅผ ์„ธ์šฐ๋А๋ƒ์— ๋‹ฌ๋ ค ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๊ณ„๊ฐ€ ๋ชจํ˜ธํ•œ ๋ชจ๋“ˆํ™”๋Š” ๊ฒฐ๊ตญ ์ฝ”๋“œ๋งŒ ๋ณต์žกํ•ด์ง„ โ€˜์ŠคํŒŒ๊ฒŒํ‹ฐ ๋ชจ๋…ธ๋ฆฌ์Šคโ€™๋กœ ํšŒ๊ท€ํ•  ๋ฟ์ž…๋‹ˆ๋‹ค. ์—”์ง€๋‹ˆ์–ด๋ง ๊ด€์ ์—์„œ ๊ฒฝ๊ณ„๋Š” ๋„๋ฉ”์ธ์˜ ์˜๋ฏธ๋ก ์  ํ•œ๊ณ„์„ ์ธ ๋™์‹œ์—, ๋ฌผ๋ฆฌ์ ์ธ ์ฝ”๋“œ ์ฐธ์กฐ๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๊ธฐ์ˆ ์  ์žฅ๋ฒฝ์ž…๋‹ˆ๋‹ค.

Bounded Context

๋ชจ๋“ˆ์˜ ๊ฒฝ๊ณ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ์ค€์€ ๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„(DDD)์˜ Bounded Context์ž…๋‹ˆ๋‹ค. ๋™์ผํ•œ ์šฉ์–ด๋ผ๋„ ๋งฅ๋ฝ์— ๋”ฐ๋ผ ๊ทธ ์˜๋ฏธ๊ฐ€ ์™„์ „ํžˆ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ์„ ์ธ์ •ํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, โ€˜์‚ฌ์šฉ์ž(User)โ€™๋ผ๋Š” ๊ฐ์ฒด๋Š” ๊ฒฐ์ œ ๋ชจ๋“ˆ์—์„œ๋Š” โ€˜๊ฒฐ์ œ ์ˆ˜๋‹จ์„ ๊ฐ€์ง„ ๊ตฌ๋งค์žโ€™์ด์ง€๋งŒ, ๋ฐฐ์†ก ๋ชจ๋“ˆ์—์„œ๋Š” โ€˜์ˆ˜์ทจ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง„ ์ˆ˜๋ น์ธโ€™์ž…๋‹ˆ๋‹ค.

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

Package Visibility

์„ค๊ณ„์ƒ์œผ๋กœ ๊ฒฝ๊ณ„๋ฅผ ํ™•์ •ํ–ˆ๋‹ค๋ฉด, ์ด๋ฅผ ์ฝ”๋“œ๋กœ ๊ฐ•์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ํŒจํ‚ค์ง€ ๊ฐ€์‹œ์„ฑ(Package Visibility) ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ๋ชจ๋“ˆ ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํด๋ž˜์Šค๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Java์˜ public ํ‚ค์›Œ๋“œ๋ฅผ ๋‚จ๋ฐœํ•˜์ง€ ์•Š๊ณ  package-private์„ ํ™œ์šฉํ•˜์—ฌ ๋ชจ๋“ˆ ๋‚ด๋ถ€์˜ ๊ตฌํ˜„์ฒด๋“ค์„ ์ˆจ๊ธฐ๋Š” ๊ฒƒ์ด ๊ทธ ์‹œ์ž‘์ž…๋‹ˆ๋‹ค.

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

๋ˆ„์ˆ˜๋˜๋Š” ์ถ”์ƒํ™”์˜ ์œ„ํ—˜

๊ฒฝ๊ณ„๊ฐ€ ๋ฌด๋„ˆ์ง€๋Š” ๊ฐ€์žฅ ํ”ํ•œ ์ง•ํ›„๋Š” ๋ชจ๋“ˆ ๋‚ด๋ถ€์˜ ์˜ˆ์™ธ(Exception) ํด๋ž˜์Šค๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ์™€ ๋ฐ€์ ‘ํ•˜๊ฒŒ ์—ฐ๊ด€๋œ ๊ฐ์ฒด๊ฐ€ ์™ธ๋ถ€๋กœ ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์™ธ๋ถ€ ๋ชจ๋“ˆ์ด ๋‚ด๋ถ€์˜ ์˜ˆ์™ธ๋ฅผ catchํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜ ๋‚ด๋ถ€ ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ ๊ตฌ์กฐ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค๋ฉด, ์ด๋ฏธ ๊ทธ ๊ฒฝ๊ณ„๋Š” ์ œ ๊ธฐ๋Šฅ์„ ์ƒ์‹คํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

๐Ÿงฉ Interface

๋ชจ๋“ˆ ๊ฐ„์˜ ๊ฒฐํ•ฉ์„ ๋А์Šจํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ํ˜‘์—…์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ์œ ์ผํ•œ ์ˆ˜๋‹จ์€ ์ธํ„ฐํŽ˜์ด์Šค(Interface)์ž…๋‹ˆ๋‹ค. ๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค์—์„œ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹จ์ˆœํžˆ โ€˜๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ์ง‘ํ•ฉโ€™์ด ์•„๋‹ˆ๋ผ, ๋‘ ๋„๋ฉ”์ธ์ด ์„œ๋กœ์—๊ฒŒ ๋™์˜ํ•œ ๋ช…์‹œ์  ๊ณ„์•ฝ(Explicit Contract)์ž…๋‹ˆ๋‹ค. ์ด ๊ณ„์•ฝ์ด ์ •๊ตํ• ์ˆ˜๋ก ์‹œ์Šคํ…œ์€ ๊ฑฐ๋Œ€ํ•œ ์ง„ํ™ ๋ฉ์–ด๋ฆฌ๊ฐ€ ์•„๋‹Œ, ์กฐ๋ฆฝ ๊ฐ€๋Šฅํ•œ ๋ถ€ํ’ˆ๋“ค์˜ ์ง‘ํ•ฉ์ฒด๋กœ ๋‚จ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Dependency Inversion

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// [Order Module] - ๋„๋ฉ”์ธ ๊ณ„์ธต์— ์œ„์น˜ํ•œ ์ธํ„ฐํŽ˜์ด์Šค
package com.example.order.domain;

public interface NotificationPort {
    void send(String message, String recipient);
}

// [Order Module] - ์„œ๋น„์Šค ๋กœ์ง์€ ์ธํ„ฐํŽ˜์ด์Šค์—๋งŒ ์˜์กด
package com.example.order.application;

import com.example.order.domain.NotificationPort;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class OrderService {
    private final NotificationPort notificationPort;

    public void completeOrder(String orderId) {
        // ... ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ...
        notificationPort.send("์ฃผ๋ฌธ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", "user@example.com");
    }
}

// [Notification Module] - ์ธํ„ฐํŽ˜์ด์Šค์˜ ์‹ค์ œ ๊ตฌํ˜„์ฒด
package com.example.notification.infrastructure;

import com.example.order.domain.NotificationPort;
import org.springframework.stereotype.Component;

@Component
public class EmailNotificationAdapter implements NotificationPort {
    @Override
    public void send(String message, String recipient) {
        // ์‹ค์ œ ์ด๋ฉ”์ผ ๋ฐœ์†ก SMTP ๋กœ์ง
    }
}

DTO Mapping

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

๋”ฐ๋ผ์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ๋Š” ์˜ค์ง ๋ฐ์ดํ„ฐ ์ „๋‹ฌ๋งŒ์„ ๋ชฉ์ ์œผ๋กœ ํ•˜๋Š” DTO(Data Transfer Object)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ชจ๋“ˆ์€ ์ธํ„ฐํŽ˜์ด์Šค ํ˜ธ์ถœ ์ง์ „์— ์ž์‹ ์˜ ๋‚ด๋ถ€ ๋ชจ๋ธ์„ DTO๋กœ ๋ณ€ํ™˜(Mapping)ํ•˜๊ณ , ๋ฐ›๋Š” ์ชฝ ์—ญ์‹œ DTO๋ฅผ ์ž์‹ ์˜ ๋‚ด๋ถ€ ๋ชจ๋ธ๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ณ€ํ™˜ ๊ณผ์ •์€ ๋ฒˆ๊ฑฐ๋กœ์›Œ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฐ ๋ชจ๋“ˆ์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ง„ํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” โ€˜๋””์ปคํ”Œ๋ง ๋น„์šฉโ€™์œผ๋กœ์„œ ์ถฉ๋ถ„ํ•œ ๊ฐ€์น˜๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

๊ณต์œ  ์ปค๋„์˜ ์œ ํ˜น

์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์—์„œ ๊ณตํ†ต์œผ๋กœ ์“ฐ์ด๋Š” Money๋‚˜ Address ๊ฐ™์€ ๊ฐ’ ๊ฐ์ฒด(Value Object)๋ฅผ โ€˜Commonโ€™ ํŒจํ‚ค์ง€์— ๋ชฐ์•„๋„ฃ๊ณ  ์‹ถ์€ ์œ ํ˜น์ด ์ƒ๊น๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด โ€˜Commonโ€™ ํŒจํ‚ค์ง€๊ฐ€ ๋น„๋Œ€ํ•ด์งˆ์ˆ˜๋ก ๋ชจ๋“  ๋ชจ๋“ˆ์ด ์ด ํŒจํ‚ค์ง€์— ์˜์กดํ•˜๊ฒŒ ๋˜์–ด, ๊ฒฐ๊ตญ ๋˜ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ๊ฐ•๊ฒฐํ•ฉ์„ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ์ •๋ง ๊ณตํ†ต์ ์ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ์ฐจ๋ผ๋ฆฌ ๊ฐ ๋ชจ๋“ˆ์ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์ค‘๋ณตํ•ด์„œ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ๋Š” ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿงฉ Dependency Management

๋ชจ๋“ˆํ™”๋œ ์‹œ์Šคํ…œ์˜ ์ˆ˜๋ช…์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฐ€์žฅ ๊ฒฐ์ •์ ์ธ ์ง€ํ‘œ๋Š” ์˜์กด์„ฑ์˜ ๋ณต์žก๋„์ž…๋‹ˆ๋‹ค. ์•„๋ฌด๋ฆฌ ๋„๋ฉ”์ธ์„ ์ž˜ ๋‚˜๋ˆ„๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •๊ตํ•˜๊ฒŒ ์„ค๊ณ„ํ–ˆ๋”๋ผ๋„, ๋ชจ๋“ˆ ๊ฐ„์˜ ์˜์กด์„ฑ ํ๋ฆ„์ด ๋ฌด์งˆ์„œํ•˜๊ฒŒ ์—‰ํ‚ค๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ์‹œ์Šคํ…œ์€ ๊ฑฐ๋Œ€ํ•œ ์ŠคํŒŒ๊ฒŒํ‹ฐ๋กœ ํšŒ๊ท€ํ•ฉ๋‹ˆ๋‹ค. ์—”์ง€๋‹ˆ์–ด๋ง ์‹ค๋ฌด์—์„œ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋Š” ๋‹จ์ˆœํžˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ–‰์œ„๊ฐ€ ์•„๋‹ˆ๋ผ, ์‹œ์Šคํ…œ์˜ ๋น„์ˆœํ™˜ ๋ฐฉํ–ฅ์„ฑ ๊ทธ๋ž˜ํ”„(DAG)๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋Š์ž„์—†๋Š” ํˆฌ์Ÿ์ž…๋‹ˆ๋‹ค.

Cyclic Dependency

์ˆœํ™˜ ์ฐธ์กฐ(Cyclic Dependency)๋Š” ๋ชจ๋“ˆํ™”์˜ ๊ฐ€์žฅ ํฐ ์ ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“ˆ A๊ฐ€ B์— ์˜์กดํ•˜๊ณ , B๊ฐ€ ๋‹ค์‹œ A์— ์˜์กดํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ํ˜•์„ฑ๋˜๋Š” ์ˆœ๊ฐ„, ๋‘ ๋ชจ๋“ˆ์€ ๋…ผ๋ฆฌ์ ์œผ๋กœ ํ•˜๋‚˜๊ฐ€ ๋˜์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ์ด๋ฅผ โ€˜์ˆœํ™˜ ์ฐธ์กฐ์˜ ์ €์ฃผโ€™๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์ด ์ƒํƒœ์—์„œ๋Š” ํŠน์ • ๋ชจ๋“ˆ๋งŒ ๋–ผ์–ด๋‚ด์–ด ํ…Œ์ŠคํŠธํ•˜๊ฑฐ๋‚˜ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์ง€๋ฉฐ, ์ฝ”๋“œ ๋ณ€๊ฒฝ์˜ ์˜ํ–ฅ ๋ฒ”์œ„๊ฐ€ ์›์„ ํƒ€๊ณ  ๋ฌดํ•œํžˆ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค.

์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ๋Š์–ด๋‚ด๋Š” ๊ฐ€์žฅ ํ‘œ์ค€์ ์ธ ๋ฐฉ๋ฒ•์€ ์•ž์„œ ์–ธ๊ธ‰ํ•œ ์˜์กด์„ฑ ์—ญ์ „(DIP)์„ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜, ๊ณตํ†ต๋œ ๋กœ์ง์„ ์ œ3์˜ ๋ชจ๋“ˆ๋กœ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋” ์ค‘์š”ํ•œ ๊ฒƒ์€ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์ „์— ์ด๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์ฐจ๋‹จํ•˜๋Š” ์‹œ์Šคํ…œ์  ์žฅ์น˜์ž…๋‹ˆ๋‹ค. ๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค์—์„œ๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„ ํ˜น์€ ๋นŒ๋“œ ํƒ€์ž„์— ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ˆœํ™˜ ๊ตฌ์กฐ๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด ์ฆ‰์‹œ ๋นŒ๋“œ๋ฅผ ์‹คํŒจ์‹œํ‚ค๋Š” ์—„๊ฒฉํ•œ ์ •์ฑ…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Architecture Guardrail (ArchUnit)

์‚ฌ๋žŒ์˜ ์˜์ง€๋Š” ๋ฏฟ์„ ๋Œ€์ƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์— ์ƒˆ๋กœ ํ•ฉ๋ฅ˜ํ•œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ ๊ฒฝ๊ณ„๋ฅผ ์นจ๋ฒ”ํ•˜๊ฑฐ๋‚˜ ํŽธ๋ฆฌํ•จ์„ ์œ„ํ•ด ์ž˜๋ชป๋œ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ํ”ํ•œ ์ผ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” ์•„ํ‚คํ…์ฒ˜ ๊ฐ€๋“œ๋ ˆ์ผ(Architecture Guardrail)์„ ์ž๋™ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Java ์ง„์˜์˜ ArchUnit์€ ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™์„ ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. โ€œ์ฃผ๋ฌธ ๋ชจ๋“ˆ์€ ๋ฐฐ์†ก ๋ชจ๋“ˆ์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์ฒด์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹คโ€๋ผ๊ฑฐ๋‚˜ โ€œ๋ชจ๋“  Controller๋Š” Service ๊ณ„์ธต๋งŒ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹คโ€์™€ ๊ฐ™์€ ๊ทœ์น™์„ ์ฝ”๋“œ๋กœ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ โ€˜Architecture as Codeโ€™ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ฝ”๋“œ ๋ฆฌ๋ทฐ์— ์†Œ์š”๋˜๋Š” ์—๋„ˆ์ง€๋ฅผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘์‹œํ‚ค๊ณ , ์•„ํ‚คํ…์ฒ˜์˜ ๋ถ€์‹์„ ์›์ฒœ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.architecture;

import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

@AnalyzeClasses(packages = "com.example")
public class ModuleBoundaryTest {

    // ๊ทœ์น™: ์ฃผ๋ฌธ(Order) ๋ชจ๋“ˆ์€ ๊ฒฐ์ œ(Payment) ๋ชจ๋“ˆ์˜ 'internal' ํŒจํ‚ค์ง€๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•  ์ˆ˜ ์—†์Œ
    @ArchTest
    static final ArchRule order_should_not_depend_on_payment_internal =
        noClasses().that().resideInAPackage("..order..")
        .should().dependOnClassesThat().resideInAPackage("..payment.internal..");

    // ๊ทœ์น™: ๋ชจ๋“  ๋ชจ๋“ˆ์€ ์„œ๋กœ์˜ 'internal' ํŒจํ‚ค์ง€๋ฅผ ์ฐธ์กฐํ•ด์„œ๋Š” ์•ˆ ๋จ (Public API๋งŒ ํ—ˆ์šฉ)
    @ArchTest
    static final ArchRule modules_should_respect_internal_encapsulation =
        noClasses().that().resideInAPackage("..internal..")
        .should().beAccessed().byAnyPackage("..internal..");
}

์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„์˜ ๋‹จ์ˆœํ™”

๋ชจ๋“ˆ์˜ ๊ฐœ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚ ์ˆ˜๋ก ์˜์กด์„ฑ ๊ด€๋ฆฌ๋Š” ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ƒ์œ„ ๋ชจ๋“ˆ์ด ํ•˜์œ„ ๋ชจ๋“ˆ์˜ ์กด์žฌ๋ฅผ ์•Œ์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๋Š” โ€˜์ด๋ฒคํŠธ ์ฃผ๋„ ํ†ต์‹ (Event-driven Communication)โ€™์„ ์ ๊ทน์ ์œผ๋กœ ๋„์ž…ํ•˜์‹ญ์‹œ์˜ค. ์ง์ ‘์ ์ธ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๋Œ€์‹  ๋ฐœํ–‰-๊ตฌ๋…(Pub-Sub) ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„์˜ ์—ฐ๊ฒฐ์„ ์„ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Logical Isolation in Shared Database

๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜์—์„œ ๊ฐ€์žฅ ํ•ด๊ฒฐํ•˜๊ธฐ ๊นŒ๋‹ค๋กœ์šด ์ง€์ ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” ๊น”๋”ํ•˜๊ฒŒ ๋‚˜๋ˆด๋”๋ผ๋„, ๋ชจ๋“  ๋ชจ๋“ˆ์ด ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ ๊ณต์œ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(Shared Database)์—์„œ ์„œ๋กœ์˜ ํ…Œ์ด๋ธ”์„ ๋งˆ๊ตฌ์žก์ด๋กœ ์กฐ์ธ(Join)ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ๊ทธ๊ฒƒ์€ โ€˜๋ถ„์‚ฐ๋œ ์ฝ”๋“œโ€™๋ฅผ ๊ฐ€์ง„ โ€˜๊ฑฐ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ๋ฉ์–ด๋ฆฌโ€™์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์ง„์ •ํ•œ ๊ฒฉ๋ฆฌ๋Š” ์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ธต์—์„œ๋„ ๋…ผ๋ฆฌ์ ์ธ ๋ฒฝ์„ ์„ธ์šธ ๋•Œ ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.

Schema Isolation

๋ฐ์ดํ„ฐ ๊ฒฉ๋ฆฌ์˜ ์ฒซ๊ฑธ์Œ์€ ๋ฌผ๋ฆฌ์ ์œผ๋กœ๋Š” ํ•˜๋‚˜์˜ DB ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋˜, ๋…ผ๋ฆฌ์ ์œผ๋กœ๋Š” ๊ฐ ๋ชจ๋“ˆ๋งŒ์ด ์ž์‹ ์˜ ํ…Œ์ด๋ธ”์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ถŒํ•œ์„ ์ œํ•œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์€ ํ…Œ์ด๋ธ” ์ด๋ฆ„์— ์ ‘๋‘์–ด(e.g., ord_, inv_)๋ฅผ ๋ถ™์ด๋Š” ๊ฒƒ์ด์ง€๋งŒ, ๋” ๊ฐ•๋ ฅํ•œ ๋ฐฉ๋ฒ•์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์Šคํ‚ค๋งˆ(Schema) ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ ๋ชจ๋“ˆ์€ ์ž์‹ ๋งŒ์˜ ์ „์šฉ ์Šคํ‚ค๋งˆ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ํƒ€ ๋ชจ๋“ˆ์˜ ์Šคํ‚ค๋งˆ์— ์žˆ๋Š” ํ…Œ์ด๋ธ”์€ ์ง์ ‘ ์กฐํšŒํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด, ๋ฐ˜๋“œ์‹œ ํ•ด๋‹น ๋ชจ๋“ˆ์ด ๋…ธ์ถœํ•œ ์ธํ„ฐํŽ˜์ด์Šค(Public API)๋ฅผ ํ†ตํ•ด ์š”์ฒญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ œ์•ฝ์€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”์–ด, ํ›—๋‚  ํŠน์ • ๋ชจ๋“ˆ์„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋กœ ๋ถ„๋ฆฌํ•ด์•ผ ํ•  ๋•Œ ๋ฐ์ดํ„ฐ ์ด๊ด€ ์ž‘์—…์˜ ๋‚œ์ด๋„๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ๋‚ฎ์ถฐ์ค๋‹ˆ๋‹ค.

Transactional Consistency

๊ณต์œ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๊ฐ€์žฅ ํฐ ์ถ•๋ณต์€ ACID ํŠธ๋žœ์žญ์…˜์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์— ๊ฑธ์นœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ, ๋ณต์žกํ•œ 2PC(Two-Phase Commit)๋‚˜ Saga ํŒจํ„ด ์—†์ด๋„ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ์™„๋ฒฝํ•˜๊ฒŒ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ์–‘๋‚ ์˜ ๊ฒ€์ž…๋‹ˆ๋‹ค.

ํŽธ๋ฆฌํ•จ์— ์ทจํ•ด ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์˜ ๋กœ์ง์„ ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ํ•œ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด๋ฒ„๋ฆฌ๋ฉด, ํŠน์ • ๋ชจ๋“ˆ์˜ ์ง€์—ฐ์ด ์ „์ฒด ์‹œ์Šคํ…œ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ๊ณ ๊ฐˆ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค์—์„œ๋„ โ€˜ํŠธ๋žœ์žญ์…˜์˜ ๋ฒ”์œ„โ€™๋Š” ์ตœ๋Œ€ํ•œ ์ข๊ฒŒ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ƒ์ ์ธ ๊ตฌ์กฐ๋Š” ์ฃผ ๋„๋ฉ”์ธ ๋กœ์ง์€ ๋กœ์ปฌ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋ถ€์ˆ˜์ ์ธ ์ž‘์—…(e.g., ์•Œ๋ฆผ ๋ฐœ์†ก, ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ)์€ ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์˜ ์ „์—ผ์„ ๋ง‰๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Cross-Module Joins: The Anti-pattern

์—”์ง€๋‹ˆ์–ด๋“ค์ด ๊ฐ€์žฅ ์ž์ฃผ ์ €์ง€๋ฅด๋Š” ์œ ํ˜น์€ ์„ฑ๋Šฅ์„ ์ด์œ ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ต์ฐจ ๋ชจ๋“ˆ ์กฐ์ธ(Cross-Module Join)์ž…๋‹ˆ๋‹ค. โ€œAPI๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ•œ ๋ฒˆ์˜ SQL ์กฐ์ธ์ด ํ›จ์”ฌ ๋น ๋ฅด๋‹คโ€๋Š” ๋…ผ๋ฆฌ๋Š” ๋‹จ๊ธฐ์ ์œผ๋กœ๋Š” ๋งž์ง€๋งŒ, ์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ๋Š” ์น˜๋ช…์ ์ž…๋‹ˆ๋‹ค.

๊ต์ฐจ ๋ชจ๋“ˆ ์กฐ์ธ์€ ๋ชจ๋“ˆ ๊ฐ„์˜ ๋ฌผ๋ฆฌ์  ๊ฒฐํ•ฉ์„ ๊ณ ์ฐฉํ™”ํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ๋ชจ๋“ˆ์˜ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ด๋ฅผ ์กฐ์ธํ•˜๋˜ ๋‹ค๋ฅธ ๋ชจ๋“  ๋ชจ๋“ˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๊นจ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์บก์Аํ™”๋ฅผ ์ •๋ฉด์œผ๋กœ ์œ„๋ฐ˜ํ•˜๋Š” ํ–‰์œ„์ž…๋‹ˆ๋‹ค.

๊ตฌ๋ถ„๊ต์ฐจ ๋ชจ๋“ˆ ์กฐ์ธ (Anti-pattern)API ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ ํ•ฉ์„ฑ (Recommended)
๊ฒฐํ•ฉ๋„๋งค์šฐ ๋†’์Œ (์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์‹œ ์—ฐ์‡„ ํŒŒ์†)๋‚ฎ์Œ (์ธํ„ฐํŽ˜์ด์Šค ๋’ค๋กœ ์ˆจ๊ฒจ์ง)
์„ฑ๋Šฅ๋‹จ์ผ ์ฟผ๋ฆฌ๋กœ ์ตœ์ ํ™” ๊ฐ€๋Šฅ๋„คํŠธ์›Œํฌ/์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐœ์ƒ
๋ถ„๋ฆฌ ๊ฐ€๋Šฅ์„ฑMSA ์ „ํ™˜ ์‹œ ์ฟผ๋ฆฌ ์ „์ฒด ์žฌ์ž‘์„ฑ ํ•„์š”์„œ๋น„์Šค ๋ถ„๋ฆฌ ์‹œ ์—”๋“œํฌ์ธํŠธ๋งŒ ๋ณ€๊ฒฝ
๊ถŒ์žฅ ์ƒํ™ฉ์ ˆ๋Œ€ ๊ธˆ์ง€ (๊ธฐ์ˆ  ๋ถ€์ฑ„์˜ ๊ทผ์›)์„ฑ๋Šฅ ์ด์Šˆ ๋ฐœ์ƒ ์‹œ ๋ณ„๋„์˜ Query Model(CQRS) ๊ตฌ์ถ•
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. Anti-pattern: DB Join์„ ํ†ตํ•œ ์กฐํšŒ (X)
// SELECT o.*, p.name FROM orders o JOIN products p ON o.product_id = p.id (X)

// 2. Recommended: ID ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋น„์Šค ๊ฒฐํ•ฉ (O)
package com.example.order.application;

import com.example.catalog.CatalogClient; // Catalog ๋ชจ๋“ˆ์˜ API
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class OrderQueryService {
    private final OrderRepository orderRepository;
    private final CatalogClient catalogClient;

    public OrderResponse getOrderDetails(String orderId) {
        Order order = orderRepository.findById(orderId).orElseThrow();
        
        // ํƒ€ ๋ชจ๋“ˆ์˜ ๋ฐ์ดํ„ฐ๋Š” DB ์กฐ์ธ์ด ์•„๋‹Œ API(ํ˜น์€ ์ง์ ‘ ์ฐธ์กฐ)๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์˜ด
        ProductDto product = catalogClient.getProductById(order.getProductId());
        
        return new OrderResponse(order, product);
    }
}

Distributed Tracing in Monolith

์•„์ด๋Ÿฌ๋‹ˆํ•˜๊ฒŒ๋„ ๋ชจ๋“ˆํ˜• ๋ชจ๋…ธ๋ฆฌ์Šค์—์„œ๋„ ๋ถ„์‚ฐ ์ถ”์ (Distributed Tracing) ๊ธฐ์ˆ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์˜ ๊ฒฝ๊ณ„๋ฅผ ๋„˜๋‚˜๋“ค๋ฉฐ ์ฒ˜๋ฆฌ๋  ๋•Œ, ์–ด๋А ๋ชจ๋“ˆ์—์„œ ๋ณ‘๋ชฉ์ด ๋ฐœ์ƒํ•˜๋Š”์ง€, ์–ด๋–ค ๋ชจ๋“ˆ์˜ ๋กœ์ง์ด ์‹คํŒจ์˜ ๊ทผ์›์ธ์ง€ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

๊ฐ ๋ชจ๋“ˆ ํ˜ธ์ถœ๋งˆ๋‹ค ๊ณ ์œ ํ•œ Trace ID๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ๋กœ๊ทธ์— ๋‚จ๊ธฐ๋Š” ์Šต๊ด€์€ ์‹œ์Šคํ…œ์˜ ๊ฐ€์‹œ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ˆœํžˆ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ๋„˜์–ด, ํ–ฅํ›„ ์‹œ์Šคํ…œ์ด ๋น„๋Œ€ํ•ด์ ธ ์‹ค์ œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋กœ ๋ถ„ํ• ๋  ๋•Œ ์„œ๋น„์Šค ๊ฐ„์˜ ํ†ต์‹  ํ๋ฆ„์„ ๋ฏธ๋ฆฌ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ท€์ค‘ํ•œ ์ง€๋„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋Š” ๋ชจ๋“ˆ์˜ ์‚ฌ์ƒํ™œ์ด๋‹ค

๋ชจ๋“ˆ์˜ ์†Œ์Šค ์ฝ”๋“œ๋Š” ๊ณต์œ ๋  ์ˆ˜ ์žˆ์–ด๋„, ๊ทธ ๋ชจ๋“ˆ์ด ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ โ€˜๋ฌผ๋ฆฌ์  ๊ตฌ์กฐโ€™๋Š” ์ฒ ์ €ํžˆ ๋น„๋ฐ€๋กœ ์œ ์ง€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์€ ๋งˆ์น˜ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ผ๊ธฐ์žฅ์„ ํ—ˆ๋ฝ ์—†์ด ์ฝ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์†Œํ†ต์€ ์˜ค์ง ์ธํ„ฐํŽ˜์ด์Šค๋ผ๋Š” ๋Œ€ํ™”๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์ด๋ฃจ์–ด์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Summary

Modular Monolithic Architecture๋Š” โ€˜์šด์˜์˜ ๋‹จ์ˆœํ•จโ€™๊ณผ โ€˜์ฝ”๋“œ์˜ ๊ฑด์ „์„ฑโ€™ ์‚ฌ์ด์—์„œ ์ตœ์ ์˜ ๊ท ํ˜•์„ ์ฐพ๋Š” ์ „๋žต์  ์„ ํƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“ˆ์„ ํ†ตํ•ด ๋„๋ฉ”์ธ์„ ๊ฒฉ๋ฆฌํ•˜๊ณ (Module), ๋‹จ์ผ ๋ฐฐํฌ๋ฅผ ํ†ตํ•ด ์šด์˜ ๋น„์šฉ์„ ์ ˆ๊ฐํ•˜๋ฉฐ(Monolith), ๋ช…ํ™•ํ•œ ์ธํ„ฐํŽ˜์ด์Šค์™€ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€๋“œ๋ ˆ์ผ์„ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ๋ถ€์‹์„ ๋ง‰์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ์ˆ˜์ค€์˜ ๋…ผ๋ฆฌ์  ๊ฒฉ๋ฆฌ๋ฅผ ์‹ค์ฒœํ•  ๋•Œ, ๋ชจ๋…ธ๋ฆฌ์Šค๋Š” ๋‹จ์ˆœํ•œ ์ฝ”๋“œ ๋ญ‰์น˜๋ฅผ ๋„˜์–ด ์–ธ์ œ๋“  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋กœ ์ง„ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐํ•œ ์ƒํƒœ๊ณ„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

References

This post is licensed under CC BY 4.0 by the author.