Communitygithub.com

領域驅動設計 (DDD) — 模式與實踐

為您的 AI 助手提供方便、即用的工程技能和最佳實踐。

領域驅動設計 (DDD) — 模式與實踐 是什麼?

此技能為 AI 代理(如 Claude Code、Cursor 等)提供領域驅動設計(DDD)的完整模式與實踐指南。它涵蓋了策略設計(限界上下文、上下文映射)和戰術設計(實體、值物件、聚合、領域事件、領域服務、儲存庫),並強調聚合根負責維護業務不變量的重要性。此技能也包含反模式(如貧血領域模型和巨型聚合)的警示,並提供基於類型安全的 TypeScript/JavaScript 環境的實際程式碼範例。其核心價值在於,透過清晰的目錄結構和依賴規則(Clean Architecture),幫助開發者建立複雜的企業級應用,確保領域邏輯與基礎設施框架的徹底分離,避免抽象洩漏。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add kok-o/koko-contextos-agents

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

領域驅動設計 (DDD) — 模式與實踐 是做什麼的?

何時使用 DDD

使用時機:

  • 超越 CRUD 的複雜業務邏輯
  • 多位領域專家使用不同詞彙
  • 領域模型是競爭優勢
  • 企業級應用程式

不使用的時機:

  • 簡單的 CRUD 應用程式
  • 黑客松 / MVP(過於繁重)
  • 沒有領域專家可用

策略設計

限界上下文 (Bounded Contexts)

DDD 中最重要的一個概念。限界上下文是一個邊界,在其中定義並適用特定的模型。

範例 — 電子商務:

[訂單上下文]          [付款上下文]       [運送上下文]
  - 訂單 (Order)        - 付款 (Payment)      - 運送 (Shipment)
  - 訂單項目 (OrderItem) - 交易 (Transaction)  - 追蹤編號 (TrackingNumber)
  - 客戶 (Customer, ref)- 退款 (Refund)       - 地址 (Address)
  - 地址 (Address, value)- 發票 (Invoice)     - 運送商 (Carrier)

Customer 在各個上下文中含義不同:

  • 訂單上下文:姓名、電子郵件、運送偏好
  • 付款上下文:帳單資訊、付款方式
  • 支援上下文:工單歷史、滿意度分數

上下文映射 (Context Map)

[訂單] ←→ [付款]     # 合作夥伴關係 (Partnership)
[訂單] → [運送]     # 客戶-供應商 (Customer-Supplier)
[訂單] → [舊版CRM]  # 防腐層 (Anti-Corruption Layer)

戰術設計

實體 (Entities)

具有身份的物件。兩個屬性相同但 ID 不同的實體是不同的。

class User {
  readonly id: UserId;
  name: string;
  email: Email;  // 值物件
}

值物件 (Value Objects)

由其屬性而非身份定義的物件。不可變。

class Email {
  constructor(readonly value: string) {
    if (!isValidEmail(value)) throw new InvalidEmailError(value);
  }
  equals(other: Email): boolean {
    return this.value === other.value;
  }
}

聚合 (Aggregates)

由單一根實體(聚合根)組成的實體和值物件的叢集。所有存取都通過根進行。

class Order {  // 聚合根
  private items: OrderItem[] = [];
  
  addItem(product: ProductRef, quantity: number): void {
    // 業務邏輯放在此處,不在服務中
    if (quantity <= 0) throw new InvalidQuantityError();
    this.items.push(new OrderItem(product, quantity));
  }
  
  get total(): Money {
    return this.items.reduce((sum, item) => sum.add(item.subtotal), Money.zero());
  }
}

聚合規則:

  1. 僅透過 ID 引用其他聚合
  2. 每次交易僅操作一個聚合
  3. 聚合間最終一致性

領域事件 (Domain Events)

在領域中發生的、領域專家關心的某些事件。

class OrderPlaced implements DomainEvent {
  constructor(
    readonly orderId: OrderId,
    readonly customerId: CustomerId,
    readonly total: Money,
    readonly occurredAt: Date
  ) {}
}

領域服務 (Domain Services)

不自然歸屬於實體或值物件的業務邏輯。

class PricingService {
  calculatePrice(order: Order, customer: Customer, promotions: Promotion[]): Money {
    // 涉及多個聚合的複雜定價邏輯
  }
}

儲存庫 (Repositories)

資料存取的抽象。每個聚合根一個儲存庫。

interface OrderRepository {
  findById(id: OrderId): Promise<Order | null>;
  save(order: Order): Promise<void>;
  delete(id: OrderId): Promise<void>;
}

目錄結構 (DDD)

src/
├── modules/
│   └── orders/                    # 限界上下文
│       ├── domain/
│       │   ├── entities/
│       │   │   └── order.ts       # 聚合根
│       │   ├── value-objects/
│       │   │   └── money.ts
│       │   ├── events/
│       │   │   └── order-placed.ts
│       │   ├── services/
│       │   │   └── pricing.ts
│       │   └── repositories/
│       │       └── order.repository.ts  # 介面
│       ├── application/
│       │   ├── commands/
│       │   │   └── place-order.ts
│       │   ├── queries/
│       │   │   └── get-order.ts
│       │   └── handlers/
│       │       └── place-order.handler.ts
│       └── infrastructure/
│           ├── persistence/
│           │   └── order.repository.impl.ts  # 實作
│           └── api/
│               └── orders.controller.ts

Clean Architecture 依賴規則

在 DDD 中,依賴關係必須嚴格指向內層:

[ 框架與驅動 (Web, DB, UI) ]
      └──▶ [ 介面適配器 (Controllers, Gateways) ]
            └──▶ [ 應用層 (Use Cases, CQRS Handlers) ]
                  └──▶ [ 領域層 (Entities, Value Objects) ]
  • 領域層對 ORM(Prisma, TypeORM)、HTTP 框架(Express, NestJS)或外部 SDK 零依賴。
  • 儲存庫在領域層/應用層中定義為介面,並在基礎設施層中實作。

領域事件 vs 整合事件

  1. 領域事件:表示單一限界上下文內部的狀態變更。
    • 直接在聚合根內部引發(order.addItem(...) 引發 OrderItemAdded)。
    • 在交易提交前於處理程序中分派。
  2. 整合事件:跨限界上下文邊界發布,以與其他服務通訊。
    • 透過事務性發件箱模式分派到訊息代理。
    • 必須使用向後相容的結構化版本。

防腐層 (Anti-Corruption Layer, ACL)

從外部限界上下文或第三方供應商 API(例如 Stripe, Salesforce)消費資料時:

  • 絕對不要將外部領域模型直接匯入您的領域。
  • 在基礎設施層建立 ACL 轉換器/適配器,將外部 DTO 轉換為您自己的值物件和實體。

反模式

  • [失敗] 貧血領域模型 — 實體僅有 getter/setter,所有邏輯都在服務中
  • [失敗] 巨型聚合 — 聚合應該小而專注於不變量
  • [失敗] 跨聚合交易 — 使用最終一致性
  • [失敗] 到處使用 DDD — 僅在複雜度證明合理時使用 DDD
  • [失敗] ORM 實體洩漏到領域中 — 領域實體不得依賴 @Entity() 或 ORM 裝飾器

程式碼範例

詳見 EXAMPLES.md 中的詳細程式碼範例。

驗證檢查清單

在完成任務前檢討階段應驗證的項目。

常見錯誤

要明確避免的反模式和事項。詳見 TROUBLESHOOTING.md。

整合說明

此技能如何與其他技能互動。

相關技能