--- id: wiki-2026-0508-inventory-management-example title: Inventory Management Example category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Inventory Domain Model, 재고 관리 예제] duplicate_of: none source_trust_level: B confidence_score: 0.85 verification_status: applied tags: [example, domain-modeling, typescript, ddd] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: Zod --- # Inventory Management Example ## 매 한 줄 > **"매 SKU · stock · reservation 의 type-safe domain model 의 walking example"**. 매 branded types · discriminated unions · runtime validation 매 결합 — 2026 modern TS pattern 의 canonical illustration. ## 매 핵심 ### 매 도메인 entities - `SKU` (branded string) - `Stock` (positive int) - `Reservation` (id, sku, qty, expiresAt) - `InventoryEvent` (Tagged: Received | Reserved | Shipped | Cancelled) ### 매 invariants - 매 stock ≥ 0 항상. - 매 reservation 의 release 후 stock 회복. - 매 ship 의 reservation 의 존재 시. ### 매 응용 1. E-commerce checkout flow (재고 차감 · 복구). 2. Warehouse management (multi-location). 3. Event-sourced inventory ledger. ## 💻 패턴 ### Branded SKU ```ts type SKU = string & { readonly __brand: "SKU" } const SkuSchema = z.string().regex(/^[A-Z]{3}-\d{6}$/).brand("SKU") const sku = SkuSchema.parse("ABC-123456") // SKU ``` ### Stock value object ```ts class Stock { private constructor(public readonly value: number) {} static of(n: number): Stock { if (!Number.isInteger(n) || n < 0) throw new Error("invalid stock") return new Stock(n) } reserve(qty: number): Stock { return Stock.of(this.value - qty) } release(qty: number): Stock { return Stock.of(this.value + qty) } } ``` ### Discriminated event union ```ts type InventoryEvent = | { type: "Received"; sku: SKU; qty: number; at: Date } | { type: "Reserved"; sku: SKU; qty: number; reservationId: string } | { type: "Shipped"; reservationId: string } | { type: "Cancelled"; reservationId: string } function reduce(state: Map, e: InventoryEvent): Map { switch (e.type) { case "Received": return new Map(state).set(e.sku, (state.get(e.sku) ?? Stock.of(0)).release(e.qty)) case "Reserved": return new Map(state).set(e.sku, state.get(e.sku)!.reserve(e.qty)) case "Shipped": case "Cancelled": return state } } ``` ### Reservation lifecycle (Result type) ```ts type Result = { ok: true; value: T } | { ok: false; error: E } function reserve(stock: Stock, qty: number): Result { if (stock.value < qty) return { ok: false, error: "INSUFFICIENT" } return { ok: true, value: stock.reserve(qty) } } ``` ### Zod runtime parse (API boundary) ```ts const ReserveCmd = z.object({ sku: SkuSchema, qty: z.number().int().positive(), customerId: z.string().uuid(), }) app.post("/reserve", (req, res) => { const cmd = ReserveCmd.safeParse(req.body) if (!cmd.success) return res.status(400).json(cmd.error.flatten()) // ... cmd.data is fully typed }) ``` ### Exhaustive switch guard ```ts function never(x: never): never { throw new Error(`unhandled: ${x}`) } // switch default → never(e) 매 새 event 추가 시 compile error ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Single-location inventory | In-memory `Map` | | Multi-location | Add `LocationId` brand · partition state | | Audit-required | Event sourcing (full event log) | | High-concurrency | Optimistic concurrency token + retry | **기본값**: event-sourced reduce 매 audit + replay benefit. 매 단순 case도 future-proof. ## 🔗 Graph - 부모: [[Domain-Driven Design]] · [[견고한 도메인 모델 및 API 계약 설계]] - 변형: [[Result Type]] · [[Discriminated_Unions|Discriminated Unions]] - 응용: [[Zod 파싱과 브랜디드 타입을 결합한 런타임 데이터 검증]] - Adjacent: [[브랜디드 타입 (Branded Types)]] · [[ts-brand]] · [[완전성 검사(Exhaustiveness Checking)]] ## 🤖 LLM 활용 **언제**: 매 type-safe domain modeling 의 teaching example 으로 reuse. **언제 X**: 매 production app 의 직접 copy — 매 oversimplified. ## ❌ 안티패턴 - **Plain `number` for stock**: 매 invariant 의 enforce 의 X — class · brand 의 사용. - **Stringly-typed events**: 매 discriminated union 의 사용. - **Skipping runtime parse at boundary**: 매 type erasure 후 — Zod / Effect Schema 의 필수. ## 🧪 검증 / 중복 - Verified (DDD blue book · Effect/Zod docs · TypeScript handbook 2026). - 신뢰도 B (illustrative example, not canonical implementation). ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full domain example with brands · DU · event sourcing |