ドメイン駆動設計 (DDD) は、コア ビジネス ドメインとそれに関連するロジックに焦点を当て、複雑なソフトウェア システムに取り組むための強力なアプローチです。 TypeScript は、強力な型指定と最新の機能を備えており、DDD の概念を効果的に実装するための優れたツールです。この記事では、TypeScript と DDD の相乗効果を探り、デザインとコードの間のギャップを埋めるための実践的な洞察、戦略、例を提供します。
コアコンセプト
1.ユビキタス言語
共有言語を使用した開発者とドメイン専門家のコラボレーションにより、コミュニケーションの誤りが軽減されます。
2.境界付きコンテキスト
ドメインのさまざまな部分を明確に分離し、特定のコンテキスト内での自律性と明確性を確保します。
3.エンティティと値オブジェクト
4.集計
データ変更の単一単位として扱われるドメイン オブジェクトのクラスター。
5.リポジトリ
永続化ロジックを抽象化し、集計へのアクセスを提供します。
6.ドメインイベント
ドメイン内で重要なアクションが発生したときに発行されるシグナル。
7.アプリケーションサービス
ビジネス ワークフローとオーケストレーション ロジックをカプセル化します。
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 } }
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 でどれだけアクティブであるかを示しています...
カバー画像は、
によって OgImagemaker を使用して作成されました。
@eddyvinck 。ツールをプレゼントしてくれてありがとう???...
以上がドメイン駆動設計 (DDD) 用の TypeScriptの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。