[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-09 21:08:02 +09:00
parent f0befc887a
commit 93ec7e9056
363 changed files with 68333 additions and 64 deletions
@@ -0,0 +1,199 @@
---
id: arch-ddd-bounded-context
title: DDD — Bounded Context / Ubiquitous Language
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [architecture, ddd, vibe-coding]
tech_stack: { language: "TS", applicable_to: ["Backend"] }
applied_in: []
aliases: [DDD, bounded context, ubiquitous language, context map, anti-corruption layer]
---
# DDD — Bounded Context
> 큰 도메인을 나눔 = **Bounded Context**. 같은 단어 (Customer) 가 context 마다 다른 의미. **Context map** 으로 관계 명시. ACL = 외부 모델 변환.
## 📖 핵심 개념
- Bounded Context: 일관 model + 용어가 통하는 경계.
- Ubiquitous Language: business + dev 같은 용어.
- Context Map: 여러 context 의 관계 (shared kernel / customer-supplier / ACL).
- Anti-Corruption Layer: 다른 context 의 모델을 우리 모델로 변환.
## 💻 코드 패턴
### Context 분리 예
```
E-commerce 큰 도메인:
Bounded Contexts:
- Catalog (Product, Category, SKU)
- Ordering (Order, Customer, Cart)
- Shipping (Shipment, Address, Carrier)
- Billing (Invoice, Payment, RefundPolicy)
- Customer (Account, Profile, Auth)
```
같은 "Customer":
- Ordering 의 Customer = 주문 history + cart
- Billing 의 Customer = 결제 method + tax id
- Shipping 의 Customer = 배송 주소
→ 같은 개념 X. 별 model.
### 폴더 / module 분리
```
src/
catalog/
domain/
application/
adapters/
ordering/
domain/
application/
adapters/
shipping/
...
```
→ Hexagonal × N (each context).
### Context map 종류
```
1. Partnership: 두 팀 같이 진화.
2. Shared Kernel: 작은 공유 model (e.g. Money, UserId type).
3. Customer-Supplier: A가 B의 요구 따름.
4. Conformist: A가 B에 일방 따름.
5. Anti-Corruption Layer: A가 B를 자기 model 로 변환.
6. Open Host Service: B가 표준 API 노출.
7. Published Language: 표준 schema (Avro / JSON Schema).
```
### Anti-Corruption Layer (ACL)
```ts
// ordering/adapters/legacyShippingAdapter.ts
import * as Legacy from 'legacy-shipping-sdk';
export class LegacyShippingAdapter implements ShippingService {
// Legacy 의 이상한 model → ordering domain 의 깔끔한 interface
async ship(order: Order): Promise<TrackingNumber> {
const legacyReq = {
shipperCode: 'X1',
itm: order.items.map(i => ({ p_id: i.productId, qt: i.qty })),
addr: this.toLegacyAddr(order.shippingAddress),
};
const r = await Legacy.client.create(legacyReq);
return new TrackingNumber(r.tn); // ordering 의 type
}
private toLegacyAddr(a: Address) { ... }
}
```
→ 이 mapper 만 수정하면 legacy 변경 차단.
### Shared Kernel
```ts
// shared/types.ts — 모든 context 가 같은
export type UserId = Brand<string, 'UserId'>;
export class Money {
constructor(public amount: Decimal, public currency: 'USD' | 'KRW') {}
}
```
작게 유지 — 너무 많이 공유 = 결합.
### Domain event (context 간 통신)
```ts
// ordering 이 OrderPlaced 발행
export class OrderPlaced {
readonly _tag = 'OrderPlaced';
constructor(public readonly orderId: string, public readonly userId: string, public readonly total: Money) {}
}
// shipping 이 listen
on(OrderPlaced, async (ev) => {
await shippingApp.scheduleShipment(ev.orderId);
});
```
→ Pub/sub 또는 outbox.
### Repository 별 context
```ts
// ordering/orderRepository.ts
interface OrderRepository {
find(id: OrderId): Promise<Order | null>; // Ordering 의 Order
}
// shipping/orderInfo.ts
interface OrderInfoService {
getShippingDetails(orderId: string): Promise<ShippingInfo>; // 다른 view
}
```
→ Shipping 의 OrderInfo 가 ordering DB 의 일부 필드만.
### Strategic vs Tactical DDD
```
Strategic: 큰 그림 (context, language, map)
Tactical: 안 ddd (entity, value object, aggregate, event, service)
```
작은 팀 = Strategic 만으로도 큰 가치.
### Module boundary 강제
```ts
// turborepo / pnpm workspaces
packages/
catalog/
ordering/
shipping/
shared/
// 의존: ordering → shared OK, ordering → catalog 직접 X
// 통신은 event 또는 published API
```
### Context 발견 (Event Storming)
```
Workshop: 도메인 전문가 + dev
- 모든 domain event 적기 (post-it)
- Group → 시간 순
- Aggregate 식별
- Context 경계 그리기
```
→ 보통 1-2 일 워크숍.
## 🤔 의사결정 기준
| 도메인 크기 | 추천 |
|---|---|
| 단순 / CRUD | DDD overkill |
| 중간 / 비즈니스 복잡 | Tactical (entity, aggregate, event) |
| 큰 / 여러 팀 | Strategic + Tactical |
| 마이크로서비스 design | Bounded context = service 경계 |
| Legacy 통합 | ACL 필수 |
| 도메인 전문가 가까이 | Ubiquitous language |
## ❌ 안티패턴
- **God context (모든 도메인 한 곳)**: 의도 안 보임. 분리.
- **Anemic domain model**: 서비스만, entity 는 데이터. 행위 entity 안.
- **Strategic skip + Tactical 만**: 결국 god.
- **Shared kernel 거대**: 사실상 monolith.
- **Repository 가 generic CRUD**: aggregate 별 의도 잃음.
- **Event 너무 잘게 (각 setter)**: noise. 비즈니스 의미만.
- **Model 직접 노출 (HTTP body)**: 도메인 변경 = API 변경.
## 🤖 LLM 활용 힌트
- 도메인 분리 → folder / module = bounded context.
- Domain event 가 context 간 통신.
- 외부 시스템 = ACL 로 변환.
## 🔗 관련 문서
- [[Arch_Hexagonal_Clean]]
- [[Arch_Aggregate_Design]]
- [[Backend_Event_Sourcing]]