์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
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 |
- react native ๋ด๋ถ ๊ตฌ์กฐ
- react native bridge
- nextjs ๋ผ์ฐํ
- nextjs route code
- react natvie
- ์ ๋ณด์ฒ๋ฆฌ๊ธฐ์ฌ
- ์๋๊ฐ์
- nextJS
- TS
- js
- typeScript
- ์ ์ฒ๊ธฐ ์ค๋น๋ฌผ
- ์ดํํฐ๋ธ ํ์ ์คํฌ๋ฆฝํธ
- ์ ์ฒ๊ธฐ ์๊ฒฉ
- ๋ถ๋ณ์
- nextjs์ฌ์ฉ์ด์
- IAP
- BOUNDED CONTEXT
- ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ
- in app purchase
- std::char_traits<unsigned char>
- rn
- HTML
- ํ์ ์คํฌ๋ฆฝํธ
- DDD
- rniap
- Aggregate
- ๋น๋ฉ ๋ธ๋ก
- ์ด๋ฒคํธ ์์คํ
- ๋น๋๊ธฐ
- Today
- Total
nika-blog
๐ ๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ(DDD) - ํํธ 2 ๋ชจ๋ธ ์ฃผ๋ ์ค๊ณ์ ๋น๋ฉ ๋ธ๋ก w. ์ฝ๋ ๋ณธ๋ฌธ
๐ ๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ(DDD) - ํํธ 2 ๋ชจ๋ธ ์ฃผ๋ ์ค๊ณ์ ๋น๋ฉ ๋ธ๋ก w. ์ฝ๋
nika0 2025. 2. 23. 17:35๐ฏ ๋๋ฉ์ธ ๋ชจ๋ธ๊ณผ ์ฝ๋์ ๊ด๊ณ
๐ DDD์ ํต์ฌ ์์น:
"๋๋ฉ์ธ ๋ชจ๋ธ๊ณผ ์ฝ๋๋ ๊ฐ์ ์๋ฏธ๋ฅผ ๊ฐ์ ธ์ผ ํ๋ค."
์ฆ, ์ฝ๋ ์์ฒด๊ฐ ๋๋ฉ์ธ ๋ชจ๋ธ์ ๋ฐ์ํด์ผ ํ๋ฉฐ, ๋ณ๋๋ก ๋ฌธ์๋ฅผ ์ ์ง๋ณด์ํ ํ์๊ฐ ์์ด์ผ ํจ.
โ
ํด๊ฒฐํด์ผ ํ ๋ ๊ฐ์ง ๋ฌธ์
1๏ธโฃ ์๊ตฌ์ฌํญ์ ์ ํฉํ ๋ชจ์ต์ผ๋ก ๋๋ฉ์ธ์ ์ด๋ป๊ฒ ๋ชจ๋ธ๋งํ ๊ฒ์ธ๊ฐ?
2๏ธโฃ ๋๋ฉ์ธ์ ๋ฐ์ํ ์ฝ๋๋ฅผ ์ด๋ป๊ฒ ๊ฐ๋ฐํ ๊ฒ์ธ๊ฐ?
๐ก ์ฝ๋๊ฐ ๋ฌธ์ ์ญํ ์ ํ๋ค๋ฉด?
โ ๋ณ๊ฒฝ๋๋ ๋ฌธ์๋ฅผ ๋ฐ๋ก ์ ์ง๋ณด์ํ ํ์ ์์ด,
โ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋๋ฉ์ธ ๋ชจ๋ธ์ ์ดํดํ ์ ์๋๋ก ์ค๊ณํ๋ ๊ฒ์ด ํต์ฌ.
๐จ ๋ชจ๋ธ ์ฃผ๋ ์ค๊ณ์ ๋น๋ฉ ๋ธ๋ก
๋น๋ฉ ๋ธ๋ก์ DDD๋ฅผ ์ฝ๋๋ก ๊ตฌํํ ๋ ๋ณต์ก๋๋ฅผ ๋ฎ์ถ๊ธฐ ์ํ ๊ฐ์ด๋๋ผ์ธ์ ๋๋ค.
๐ ํ์์ ์ธ ๊ฐ๋ ์ ์๋์ง๋ง, ์ง๊ด์ ์ธ ๊ฐ์ด๋ ์ญํ ์ ์ํ
โ ๋๋ฉ์ธ์ ํํํ๋ ๋น๋ฉ ๋ธ๋ก
- Association (์ฐ๊ด ๊ด๊ณ)
- Value Object (๊ฐ ๊ฐ์ฒด)
- Entity (์ํฐํฐ)
- Service (์๋น์ค)
- Module (๋ชจ๋)
โ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋น๋ฉ ๋ธ๋ก
- Aggregate (์ ๊ทธ๋ฆฌ๊ฒ์ดํธ)
- Repository (๋ฆฌํฌ์งํ ๋ฆฌ)
- Factory (ํฉํ ๋ฆฌ)
๐ก ๊ฒฐ๋ก : ๋๋ฉ์ธ์ ๊ฐ๋
์ ์ฝ๋๋ก ์ฎ๊ธฐ๊ธฐ ์ํ ์ง๊ด์ ์ธ ๊ฐ์ด๋!
โ ํ์ง๋ง ๋ฐ๋์ ์ด ๋ฐฉ์์ผ๋ก ๊ตฌํํด์ผ ํ๋ ๊ฒ์ ์๋.
๐ ๋ถ๋ณ์ (Invariant)
๐ ๋ถ๋ณ์์ด๋?
์ ์์ ์ธ ์ํ๋ฅผ ๊ท์ ํ๋ ๋น์ฆ๋์ค ๊ท์น
์ฆ, ๋๋ฉ์ธ ๋ชจ๋ธ์ด ํญ์ ์ ์งํด์ผ ํ๋ ์กฐ๊ฑด
โ
์์ : ์์ ์ฃผ๋ฌธ ์์คํ
1๏ธโฃ ํ๋งค ์ค์ธ ๋ฉ๋ด์๋ ์ต์
๊ทธ๋ฃน์ด ํ ๊ฐ ์ด์ ์กด์ฌํด์ผ ํ๋ค.
- โ
์: "๊ณ ๊ธฐ 1์ธ ์ธํธ"์๋ ์ผ๊ฒน์ด/๋ชฉ์ด ์ต์
์ด ์์ด์ผ ํจ
2๏ธโฃ ํ๋งค ์ค์ธ ๋ฉ๋ด์ ์ต์ ๊ฐ๊ฒฉ์ 0์ ์ด์์ด์ด์ผ ํ๋ค. - โ
์: ์ผ๊ฒน์ด ์ต์
์ด 0์ ์ด์์ด์ด์ผ ํจ
3๏ธโฃ ํ๋งค ์ค์ธ ๋ฉ๋ด์๋ ํ์ ์ต์ ๊ทธ๋ฃน์ด ํ ๊ฐ ์ด์ ์กด์ฌํด์ผ ํ๋ค. - โ ์: "๊ณ ๊ธฐ ์ข ๋ฅ" ์ต์ ์ ๋ฐ๋์ ์ ํํด์ผ ํจ
๐ ๋ถ๋ณ์์ด ๊ธฐํ ๋จ๊ณ์์ ๊ท์ ๋๊ธฐ ์ด๋ ค์ด ์ด์
โ ๊ธฐํ์์๋ ์ธ๋ถ์ ์ธ ์กฐ๊ฑด์ ๋ชจ๋ ์ ์ํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์
โ ๊ฐ๋ฐํ์ด ๊ตฌํ ๋จ๊ณ์์ ์ ํํ ๋ถ๋ณ์์ ๋์ถํ๊ณ ๊ฒ์ฆํ๋ ์ญํ ์ ์ํํด์ผ ํจ
๐ ์ํฐํฐ(Entity)์ ๊ฐ ๊ฐ์ฒด(Value Object)์ ์ฐจ์ด
๐ ์ํฐํฐ(Entity): ์๋ณ์ฑ์ด ์ค์ํ ๊ฐ์ฒด
โ ๋์ผํ ๊ฐ์ฒด๊ฐ ๊ณ์ ์ ์ง๋์ด์ผ ํ๋ ๊ฒฝ์ฐ
โ ๊ณ ์ ํ ID(์๋ณ์)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ๋ถ๋จ
โ
์์
๐ฐ "๋ด๊ฐ ๋น๋ ค์ค ๋ง ์์ง๋ฆฌ ์งํ๋ ๋ฐ๋์ ๊ฐ์ ์งํ์ฌ์ผ ํ๋ค!"
โก ์ด ๊ฒฝ์ฐ "ํ๊ธ" ๊ฐ์ฒด๋ ์ํฐํฐ(Entity)
๐ JPA ์ํฐํฐ(Entity)์ DDD ์ํฐํฐ๋ ๋ค๋ฅธ ๊ฐ๋
โ DDD ์ํฐํฐ: ๋๋ฉ์ธ ํ์ ์ค์ฌ, ๋ถ๋ณ์ ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณ
โ JPA ์ํฐํฐ: ํ
์ด๋ธ ๋งคํ ์ค์ฌ, ๋ฐ์ดํฐ ์ ์ฅ์ ์ํ ORM ๊ตฌ์กฐ
๐ก JPA๋ฅผ ์ฌ์ฉํ ๋ ์ฃผ์ํ ์
- JPA๋ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณ๋จ
- DDD์ ๊ฐ๋ ๊ณผ ์ถฉ๋ํ๋ ๊ฒฝ์ฐ๊ฐ ์์
- ์ํฐํฐ์ ๋ผ์ดํ์ฌ์ดํด์ ๋ช ํํ๊ฒ ์ ์ํด์ผ ์ ์ง๋ณด์์ฑ์ด ๋์์ง
๐ ๊ฐ ๊ฐ์ฒด(Value Object): ์๋ณ์ฑ์ด ํ์ ์๋ ๊ฐ์ฒด
โ ๊ฐ์ฒด์ ์์ฑ์ด ์ค์ํ๊ณ , ๊ณ ์ ํ ์๋ณ์๋ ํ์ ์์
โ ๊ฐ์ด ๋์ผํ๋ฉด ๊ฐ์ ๊ฐ์ฒด๋ก ๊ฐ์ฃผ
โ
์์
๐ณ "๋ด๊ฐ ๋น๋ ค์ค ๋ง ์์ง๋ฆฌ ์งํ๋ฅผ ๋ค์ด๋ฒ ํ์ด๋ก ๋ฐ์๋ ๋๋ค."
โก ์ด ๊ฒฝ์ฐ "๊ธ์ก(Money)"์ ๊ฐ ๊ฐ์ฒด(Value Object)
๐ก ์ํฐํฐ๋ณด๋ค ๊ฐ ๊ฐ์ฒด๊ฐ ๋ ๋ค๋ฃจ๊ธฐ ์ฌ์!
โก ์ฒ์์๋ ๊ฐ์ ๊ฐ์ฒด๋ก ๋ง๋ค๊ณ , ํ์ํ ๋ ์ํฐํฐ๋ก ๋ณ๊ฒฝํ๋ ๊ฒ์ด ์ข์.
๐ก DDD ๊ด์ ์์ ๊ฐ ๊ฐ์ฒด๋ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅ(Immutable)ํด์ผ ํ๋ค.
- ๋ง์ฝ ๊ฐ ๊ฐ์ฒด์ ๊ฐ์ ์์ ํด์ผ ํ๋ค๋ฉด,
๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ์ญ์ ํ๊ณ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํด์ผ ํจ - DB ๊ด์ ์์๋ trade-off๊ฐ ์์
- ๊ฐ ๊ฐ์ฒด๋ฅผ ์ญ์ ํ ๋ค์ ์ ์ฅํ๋ฉด ๋ถํ์ํ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ๋ฐ์
- ํ์ง๋ง, DDD์์๋ ๋น์ฆ๋์ค ๋ชจ๋ธ์ ์ ์งํ๋ ๊ฒ์ด ๋ ์ค์
โ ์์ : ์ฃผ๋ฌธ ํญ๋ชฉ(OrderItem)
- price๋ฅผ ์์ ํ ๋, ๊ธฐ์กด OrderItem์ ์ญ์ ํ๊ณ ์๋ก์ด OrderItem์ ์์ฑํด์ผ ํจ
- DB์์๋ ์ด๋ฅผ UPDATE ๋์ DELETE + INSERT๋ก ์ฒ๋ฆฌํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์
๐ ์ฐ๊ด ๊ด๊ณ์ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ (Aggregate)
๐ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ๋?
โ ์ํฐํฐ์ ๊ฐ ๊ฐ์ฒด๋ฅผ ๋
ผ๋ฆฌ์ ์ผ๋ก ๋ฌถ์ด์ ๋จ์ํํ๋ ๊ฐ๋
โ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋จ์๋ฅผ ๊ฒฐ์ ํ๋ ์ญํ
๐ ์ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ๊ฐ ํ์ํ ๊น?
- ๋๋ฉ์ธ ๋ชจ๋ธ์ด ๋ณต์กํ ์๋ก, ์ฐ๊ด ๊ด๊ณ๋ฅผ ๋จ์ํํด์ผ ํจ
- ์ํฐํฐ ๊ฐ ์ง์ ์ ์ธ ์ฐ๊ด ๊ด๊ณ๋ฅผ ๋งบ์ผ๋ฉด ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง
โ
์์ : ์์ ์ฃผ๋ฌธ ์์คํ
โ "์ฃผ๋ฌธ(Order) - ์ฃผ๋ฌธ ํญ๋ชฉ(OrderLineItem)"
โ ์ฃผ๋ฌธ ํญ๋ชฉ์ ์ฃผ๋ฌธ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ ๋ด์์ ๊ด๋ฆฌ๋จ
โ ๊ณ ๊ฐ(Customer)๊ณผ ์ฃผ๋ฌธ(Order)์ ๋ณ๊ฐ์ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ๋ก ๋ถ๋ฆฌ
๐ก ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ๋ฅผ ์ ์ํ๋ฉด ์ฐ๊ด ๊ด๊ณ๋ฅผ ์ต์ํํ์ฌ, ์ฝ๋ ๋ณต์ก์ฑ์ ์ค์ผ ์ ์์.
๐ ์ฝ๋ ์์ : ๋ฐฐ๋ฌ ๋๋ฉ์ธ
๐ ์ฃผ๋ฌธ(Order) ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ
class Order {
private orderId: string;
private customerId: string;
private items: OrderItem[];
private status: OrderStatus;
constructor(orderId: string, customerId: string, items: OrderItem[]) {
if (items.length === 0) {
throw new Error("์ฃผ๋ฌธ์๋ ์ต์ ํ ๊ฐ ์ด์์ ์ํ์ด ํ์ํฉ๋๋ค.");
}
this.orderId = orderId;
this.customerId = customerId;
this.items = items;
this.status = OrderStatus.PENDING;
}
completeOrder() {
if (this.status !== OrderStatus.PENDING) {
throw new Error("์ฃผ๋ฌธ์ ์๋ฃํ ์ ์๋ ์ํ์
๋๋ค.");
}
this.status = OrderStatus.COMPLETED;
}
}
๐ ์ฃผ๋ฌธ ํญ๋ชฉ(OrderItem) - ๊ฐ ๊ฐ์ฒด
class OrderItem {
private productId: string;
private quantity: number;
private price: number;
constructor(productId: string, quantity: number, price: number) {
if (quantity <= 0) {
throw new Error("์๋์ 1๊ฐ ์ด์์ด์ด์ผ ํฉ๋๋ค.");
}
if (price < 0) {
throw new Error("๊ฐ๊ฒฉ์ 0์ ์ด์์ด์ด์ผ ํฉ๋๋ค.");
}
this.productId = productId;
this.quantity = quantity;
this.price = price;
}
getTotalPrice(): number {
return this.quantity * this.price;
}
}
๐ฆ ๋ฆฌํฌ์งํ ๋ฆฌ (Repository)
class OrderRepository {
private orders: Map<string, Order> = new Map();
save(order: Order) {
this.orders.set(order.getOrderId(), order);
}
findById(orderId: string): Order | undefined {
return this.orders.get(orderId);
}
}
๐ง Factory ํจํด (๊ฐ์ฒด ์์ฑ ๋ก์ง์ ๋ถ๋ฆฌ)
๐ Factory ํจํด์ด ํ์ํ ์ด์
โ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ ์์ฑ์ด ๋ณต์กํ ๋, ์ด๋ฅผ ์บก์ํํ๊ธฐ ์ํด ์ฌ์ฉ
โ ์ธ๋ถ DB ์กฐํ๊ฐ ํ์ํ๊ฑฐ๋ ๋ถ๋ณ์์ด ๋ณต์กํ ๊ฒฝ์ฐ Factory๋ก ์ด๊ด
โ ์์ : ์ฅ๋ฐ๊ตฌ๋ (Cart)
class CartFactory {
static createCart(customerId: string, items: CartItem[]): Cart {
if (items.length === 0) {
throw new Error("์ฅ๋ฐ๊ตฌ๋์๋ ์ต์ ํ ๊ฐ์ ์ํ์ด ์์ด์ผ ํฉ๋๋ค.");
}
return new Cart(customerId, items);
}
}
๐ก Factory๋ฅผ ํ์ฉํ๋ฉด ๋๋ฉ์ธ ๊ฐ์ฒด์ ์์ฑ์ ํ ๊ณณ์์ ๊ด๋ฆฌํ ์ ์์.
๐ฏ ๋๋ฉ์ธ ์๋น์ค (Domain Service)
๐ ๋๋ฉ์ธ ์๋น์ค๋?
โ ํน์ ์ํฐํฐ์ ์ํ์ง ์๋ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ์๋น์ค
โ ๊ฐ์ฒด ๊ฐ์ ์ฑ
์์ด ๋ช
ํํ์ง ์์ ๋, ๋ณ๋์ ์๋น์ค๋ก ๋ถ๋ฆฌ
โ ๊ผญ ํ์ํ์ง ๊ฒํ ํด ๋ณผ ํ์ ์์
โ ์์ : ํ ์ธ ๋ก์ง (Discount Service)
class DiscountService {
static applyDiscount(order: Order, discountRate: number): number {
if (discountRate < 0 || discountRate > 1) {
throw new Error("ํ ์ธ์จ์ 0~1 ์ฌ์ด์ ๊ฐ์ด์ด์ผ ํฉ๋๋ค.");
}
return order.getTotalPrice() * (1 - discountRate);
}
}
๐ก ํน์ ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ์ ํฌํจํ๊ธฐ ์ด๋ ค์ด ๋ก์ง์ ๋๋ฉ์ธ ์๋น์ค๋ก ๋ถ๋ฆฌํ ์ ์์.
๐ ๊ฒฐ๋ก : DDD์ ๋น๋ฉ ๋ธ๋ก์ ํ์ฉํ๋ฉด?
โ
๋๋ฉ์ธ ๋ชจ๋ธ๊ณผ ์ฝ๋๋ฅผ ์ผ์น์์ผ ์ ์ง๋ณด์ ๋ถ๋ด์ ์ค์ผ ์ ์์
โ
์ํฐํฐ์ ๊ฐ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ถํ์ฌ ์๋ณ์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์
โ
์ ๊ทธ๋ฆฌ๊ฒ์ดํธ๋ฅผ ํ์ฉํ์ฌ ์ฐ๊ด ๊ด๊ณ๋ฅผ ๋จ์ํํ๊ณ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๊ฒฝ๊ณ๋ฅผ ์ค์ ํ ์ ์์
โ
Factory๋ฅผ ํ์ฉํ์ฌ ๋ณต์กํ ๊ฐ์ฒด ์์ฑ์ ์บก์ํํ ์ ์์
โ
๋๋ฉ์ธ ์๋น์ค๋ก ํน์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌํ ์ ์์
๐ก DDD์ ๋น๋ฉ ๋ธ๋ก์ ๋ณต์กํ ๋๋ฉ์ธ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋๋ก ๋์์ฃผ๋ ๊ฐ๋ ฅํ ๊ฐ์ด๋์ ๋๋ค! ๐