Chuyển đến nội dung chính

レッスン 18: システム アーキテクチャのドメイン駆動設計

DDD 戦略パターン: 境界コンテキスト、ユビキタス言語、コンテキスト マップ。 DDD 戦術パターン: エンティティ、値オブジェクト、集約、リポジトリ。 DDD を適用して、マイクロサービスのサービス境界を定義します。

🏗️ アーキテクチャ — レッスン 18 レッスン 18: システムのドメイン駆動設計 建築

システムアーキテクチャ: ゼロからヒーローへ

パート 5: アーキテクチャ パターン

xdev.asia

はじめに

DDD (ドメイン駆動設計) は、ビジネス ドメインに焦点を当てたソフトウェア設計手法です。システム アーキテクチャでは、DDD は、マイクロサービスを設計する際に最も難しい問題である サービス境界 を定義するのに役立ちます。


1. 戦略的パターン

1.1 ユビキタス言語

Vấn đề: Developers và Business nói khác ngôn ngữ

  Business: "Khi khách hàng đặt hàng..."
  Developer: "Khi user tạo order record..."

  Business: "Sản phẩm hết hàng"
  Developer: "Product inventory count = 0"

Ubiquitous Language:
  Thống nhất ngôn ngữ giữa dev và business
  Dùng CÙNG thuật ngữ trong code, docs, meetings

  // Tốt - dùng domain language
  class Order {
    placeOrder()
    cancelOrder()
    shipOrder()
  }

  // Xấu - dùng technical language
  class OrderDAO {
    insertRecord()
    deleteRecord()
    updateStatus()
  }

1.2 境界付きコンテキスト

"Product" nghĩa khác nhau tùy context:

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│ Catalog Context │  │ Inventory Context│  │ Pricing Context │
│                 │  │                  │  │                 │
│ Product:        │  │ Product:         │  │ Product:        │
│ - name          │  │ - SKU            │  │ - base_price    │
│ - description   │  │ - quantity       │  │ - discount      │
│ - images        │  │ - warehouse      │  │ - margin        │
│ - categories    │  │ - reorder_level  │  │ - tax_rate      │
│ - reviews       │  │ - supplier       │  │ - currency      │
└─────────────────┘  └─────────────────┘  └─────────────────┘

Mỗi Bounded Context:
  - Có MODEL riêng cho cùng concept
  - Có NGÔN NGỮ riêng
  - Có DATABASE riêng
  → Tương ứng 1 Microservice!

1.3 コンテキストマップ

Các Bounded Contexts quan hệ thế nào?

┌───────────┐         ┌───────────┐
│  Catalog  │◄─ ACL ─►│ Inventory │
│  Context  │         │  Context  │
└─────┬─────┘         └───────────┘
      │
  Conformist
      │
┌─────▼─────┐         ┌───────────┐
│  Order    │──OHS/PL─►│  Payment  │
│  Context  │         │  Context  │
└─────┬─────┘         └───────────┘
      │
  Partnership
      │
┌─────▼─────┐
│ Shipping  │
│  Context  │
└───────────┘

Relationships:
  - Partnership: Hai teams hợp tác chặt
  - Customer-Supplier: Downstream phụ thuộc Upstream
  - Conformist: Downstream chấp nhận model của Upstream
  - ACL (Anti-Corruption Layer): Translate giữa models
  - OHS (Open Host Service): Upstream cung cấp API chuẩn
  - PL (Published Language): Schema chung (JSON, Protobuf)

2. 戦術パターン

2.1 エンティティと値オブジェクト

Entity: Có identity, thay đổi theo thời gian
  class Order {
    id: UUID          // Identity
    status: string    // Thay đổi
    items: OrderItem[]
  }
  // 2 Orders khác nhau vì ID khác nhau

Value Object: Không có identity, immutable
  class Money {
    amount: number
    currency: string
  }
  // Money(100, "VND") == Money(100, "VND")
  // So sánh bằng VALUE, không phải reference

  class Address {
    street: string
    city: string
    country: string
  }

2.2 集計

Aggregate = cluster of entities + value objects
Có 1 Aggregate Root (entry point)
Bên ngoài chỉ access qua Aggregate Root

┌──────────────────────────────────┐
│ Order Aggregate                  │
│                                  │
│ ┌───────────────────┐           │
│ │ Order (Root)      │           │
│ │ - id              │           │
│ │ - status          │           │
│ │ - placedAt        │           │
│ │                   │           │
│ │ ┌─────────────┐   │           │
│ │ │ OrderItem   │   │  Bên ngoài│
│ │ │ - productId │   │  KHÔNG    │
│ │ │ - quantity  │   │  access   │
│ │ │ - price     │   │  trực tiếp│
│ │ └─────────────┘   │  OrderItem│
│ │                   │           │
│ │ ┌─────────────┐   │           │
│ │ │ Money       │   │           │
│ │ │ (total)     │   │           │
│ │ └─────────────┘   │           │
│ └───────────────────┘           │
└──────────────────────────────────┘

Rules:
  1. Aggregate Root enforce business rules
  2. Transactions = 1 Aggregate
  3. Reference other Aggregates bằng ID (không object reference)
  4. Consistency boundary = Aggregate boundary

2.3 リポジトリ

Repository = interface để persist/retrieve Aggregates

interface OrderRepository {
  findById(id: OrderId): Order | null
  save(order: Order): void
  findByUserId(userId: UserId): Order[]
}

// Implementation ẩn bên trong
class PostgresOrderRepository implements OrderRepository {
  async findById(id: OrderId): Promise<Order | null> {
    const row = await db.query('SELECT * FROM orders WHERE id = $1', [id]);
    return row ? this.toDomain(row) : null;
  }
}

Rule: 1 Repository per Aggregate Root
  ✅ OrderRepository
  ❌ OrderItemRepository (access qua Order)

3. DDD → マイクロサービスの境界

3.1 プロセス

Step 1: Event Storming
  Post-it notes trên tường
  Orange: Domain Events (OrderPlaced, PaymentReceived)
  Blue: Commands (PlaceOrder, ChargePayment)
  Yellow: Aggregates (Order, Payment)
  Pink: Policies / Business Rules

Step 2: Identify Bounded Contexts
  Nhóm events/commands/aggregates liên quan
  → Mỗi nhóm = 1 Bounded Context

Step 3: Define Context Relationships
  Context Map: Partnership, ACL, etc.

Step 4: Map to Services
  1 Bounded Context ≈ 1 Microservice
  (có thể split thêm nếu context quá lớn)

3.2 電子商取引の例

Event Storming Results:

  ProductAdded → ProductPriceChanged → ProductSearched
  → Catalog Context (Service)

  InventoryReceived → StockReserved → StockReleased
  → Inventory Context (Service)

  OrderPlaced → OrderConfirmed → OrderShipped → OrderDelivered
  → Order Context (Service)

  PaymentRequested → PaymentCharged → PaymentRefunded
  → Payment Context (Service)

  ShipmentCreated → ShipmentTracked → ShipmentDelivered
  → Shipping Context (Service)

Context Map:
  Order ──uses──► Catalog (to get product info)
  Order ──uses──► Inventory (to reserve stock)
  Order ──uses──► Payment (to charge)
  Order ──uses──► Shipping (to ship)

4. 破損防止層 (ACL)

Vấn đề: External service (payment gateway) có model khác

External Payment API:
  { "transaction_id": "xxx", "amt": 100, "ccy": "USD" }

Our Domain:
  { "paymentId": "xxx", "amount": Money(100, "USD") }

ACL translates:
  ┌──────────────┐     ┌───────────┐     ┌──────────────┐
  │ Our Domain   │────►│   ACL     │────►│ External API │
  │ Payment      │     │ Translate │     │ Stripe/VNPay │
  │ Model        │◄────│ Adapter   │◄────│ Raw response │
  └──────────────┘     └───────────┘     └──────────────┘

  // ACL Implementation
  class PaymentGatewayACL {
    async charge(payment: DomainPayment): DomainResult {
      // Translate to external model
      const externalReq = {
        amt: payment.amount.value,
        ccy: payment.amount.currency,
      };
      // Call external API
      const response = await stripe.charge(externalReq);
      // Translate back to domain model
      return new DomainResult(response.transaction_id, ...);
    }
  }

概要

パターンレベル目的
境界付きコンテキスト戦略的サービス境界を決定する
コンテキストマップ戦略的サービス間の関係
ユビキタス言語戦略的言語の統一
エンティティ/値オブジェクト戦術モデルドメインオブジェクト
集計戦術一貫性 + トランザクション境界
リポジトリ戦術データアクセスの抽象化
ACL統合外部システムからドメインを保護する

演習

  1. イベントストーミング: 病院管理システム: 患者登録 → 医師の診察 → 薬の処方 → 支払い → 薬の受け取り。ドメイン イベント、コマンドをリストし、境界付きコンテキストを定義します。

  2. 集約デザイン: ショッピング カート: カートには CartItem が含まれており、各 CartItem には製品参照、数量、価格が含まれます。集合デザイン。ビジネス ルール: 最大 20 品目、数量 1 ~ 99、合計 < 100M VND。

  3. コンテキスト マップ: 統合電子商取引: 内部 (注文、カタログ、在庫)、外部 (Stripe Payment、GHN Shipping、SendGrid Email)。関係タイプを使用してコンテキスト マップを描画します。