ホームページ > ウェブフロントエンド > jsチュートリアル > ドメイン駆動設計 (DDD) 用の TypeScript

ドメイン駆動設計 (DDD) 用の TypeScript

Patricia Arquette
リリース: 2024-12-25 17:18:14
オリジナル
506 人が閲覧しました

ドメイン駆動設計 (DDD) は、コア ビジネス ドメインとそれに関連するロジックに焦点を当て、複雑なソフトウェア システムに取り組むための強力なアプローチです。 TypeScript は、強力な型指定と最新の機能を備えており、DDD の概念を効果的に実装するための優れたツールです。この記事では、TypeScript と DDD の相乗効果を探り、デザインとコードの間のギャップを埋めるための実践的な洞察、戦略、例を提供します。

ドメイン駆動設計を理解する

コアコンセプト

1.ユビキタス言語
共有言語を使用した開発者とドメイン専門家のコラボレーションにより、コミュニケーションの誤りが軽減されます。

2.境界付きコンテキスト
ドメインのさまざまな部分を明確に分離し、特定のコンテキスト内での自律性と明確性を確保します。

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

  • エンティティ: 固有の ID を持つオブジェクト。
  • 値オブジェクト: 属性によって定義される不変オブジェクト。

4.集計
データ変更の単一単位として扱われるドメイン オブジェクトのクラスター。

5.リポジトリ
永続化ロジックを抽象化し、集計へのアクセスを提供します。

6.ドメインイベント
ドメイン内で重要なアクションが発生したときに発行されるシグナル。

7.アプリケーションサービス
ビジネス ワークフローとオーケストレーション ロジックをカプセル化します。

TypeScript が DDD に適合する理由

1.静的型付け: 強力な型チェックは、ドメイン ロジックを明示的にモデル化するのに役立ちます。
2.インターフェイス: コンポーネント間のコントラクトを強制します。
3.クラス: エンティティ、値オブジェクト、集計を自然に表現します。
4.タイプ ガード: 実行時に型の安全性を確保します。
5.ユーティリティ タイプ: 動的ドメインの強力な型変換を有効にします。

実際の実装

1.エンティティのモデリング
エンティティは一意の ID を持ち、動作をカプセル化します。

class Product {
  constructor(
    private readonly id: string,
    private name: string,
    private price: number
  ) {}

  changePrice(newPrice: number): void {
    if (newPrice <= 0) {
      throw new Error("Price must be greater than zero.");
    }
    this.price = newPrice;
  }

  getDetails() {
    return { id: this.id, name: this.name, price: this.price };
  }
}
ログイン後にコピー
ログイン後にコピー



2.値オブジェクトの作成
値オブジェクトは不変であり、値によって比較されます。

class Money {
  constructor(private readonly amount: number, private readonly currency: string) {
    if (amount < 0) {
      throw new Error("Amount cannot be negative.");
    }
  }

  add(other: Money): Money {
    if (this.currency !== other.currency) {
      throw new Error("Currency mismatch.");
    }
    return new Money(this.amount + other.amount, this.currency);
  }
}
ログイン後にコピー
ログイン後にコピー



3.集計の定義
集計により、境界内のデータの一貫性が保証されます。

class Order {
  private items: OrderItem[] = [];

  constructor(private readonly id: string) {}

  addItem(product: Product, quantity: number): void {
    const orderItem = new OrderItem(product, quantity);
    this.items.push(orderItem);
  }

  calculateTotal(): number {
    return this.items.reduce((total, item) => total + item.getTotalPrice(), 0);
  }
}

class OrderItem {
  constructor(private product: Product, private quantity: number) {}

  getTotalPrice(): number {
    return this.product.getDetails().price * this.quantity;
  }
}
ログイン後にコピー
ログイン後にコピー



4.リポジトリの実装
リポジトリはデータ アクセスを抽象化します。

interface ProductRepository {
  findById(id: string): Product | null;
  save(product: Product): void;
}

class InMemoryProductRepository implements ProductRepository {
  private products: Map<string, Product> = new Map();

  findById(id: string): Product | null {
    return this.products.get(id) || null;
  }

  save(product: Product): void {
    this.products.set(product.getDetails().id, product);
  }
}
ログイン後にコピー



5.ドメイン イベントの使用
ドメイン イベントは、状態の変化をシステムに通知します。

class DomainEvent {
  constructor(public readonly name: string, public readonly occurredOn: Date) {}
}

class OrderPlaced extends DomainEvent {
  constructor(public readonly orderId: string) {
    super("OrderPlaced", new Date());
  }
}

// Event Handler Example
function onOrderPlaced(event: OrderPlaced): void {
  console.log(`Order with ID ${event.orderId} was placed.`);
}
ログイン後にコピー



6.アプリケーションサービス
アプリケーション サービスはワークフローを調整し、ユースケースを強制します。

class OrderService {
  constructor(private orderRepo: OrderRepository) {}

  placeOrder(order: Order): void {
    this.orderRepo.save(order);
    const event = new OrderPlaced(order.id);
    publishEvent(event); // Simulated event publishing
  }
}
ログイン後にコピー

7. 境界付きコンテキストの操作

TypeScript のモジュール機能を利用して、境界のあるコンテキストを分離します。

  • コンテキストごとに個別のディレクトリを使用します。
  • クロスコンテキスト通信用のインターフェイスを明示的に定義します。

構造例:

class Product {
  constructor(
    private readonly id: string,
    private name: string,
    private price: number
  ) {}

  changePrice(newPrice: number): void {
    if (newPrice <= 0) {
      throw new Error("Price must be greater than zero.");
    }
    this.price = newPrice;
  }

  getDetails() {
    return { id: this.id, name: this.name, price: this.price };
  }
}
ログイン後にコピー
ログイン後にコピー

高度な機能

柔軟なモデリングのための条件型

class Money {
  constructor(private readonly amount: number, private readonly currency: string) {
    if (amount < 0) {
      throw new Error("Amount cannot be negative.");
    }
  }

  add(other: Money): Money {
    if (this.currency !== other.currency) {
      throw new Error("Currency mismatch.");
    }
    return new Money(this.amount + other.amount, this.currency);
  }
}
ログイン後にコピー
ログイン後にコピー

検証用のテンプレート リテラル タイプ

class Order {
  private items: OrderItem[] = [];

  constructor(private readonly id: string) {}

  addItem(product: Product, quantity: number): void {
    const orderItem = new OrderItem(product, quantity);
    this.items.push(orderItem);
  }

  calculateTotal(): number {
    return this.items.reduce((total, item) => total + item.getTotalPrice(), 0);
  }
}

class OrderItem {
  constructor(private product: Product, private quantity: number) {}

  getTotalPrice(): number {
    return this.product.getDetails().price * this.quantity;
  }
}
ログイン後にコピー
ログイン後にコピー

私の個人ウェブサイト: https://shafayet.zya.me


そうですね、これはあなたが Git-toilet でどれだけアクティブであるかを示しています...

TypeScript for Domain-Driven Design (DDD)


カバー画像は、

によって OgImagemaker を使用して作成されました。 @eddyvinck 。ツールをプレゼントしてくれてありがとう???...

以上がドメイン駆動設計 (DDD) 用の TypeScriptの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート