Heim >Web-Frontend >js-Tutorial >TypeScript für Domain-Driven Design (DDD)

TypeScript für Domain-Driven Design (DDD)

Patricia Arquette
Patricia ArquetteOriginal
2024-12-25 17:18:14514Durchsuche

Domain-Driven Design (DDD) ist ein leistungsstarker Ansatz zur Bewältigung komplexer Softwaresysteme, der sich auf die Kerngeschäftsdomäne und die damit verbundene Logik konzentriert. TypeScript ist mit seiner starken Typisierung und seinen modernen Funktionen ein hervorragendes Werkzeug, um DDD-Konzepte effektiv umzusetzen. Dieser Artikel untersucht die Synergie zwischen TypeScript und DDD und bietet praktische Einblicke, Strategien und Beispiele, um die Lücke zwischen Design und Code zu schließen.

Domain-Driven Design verstehen

Kernkonzepte

1. Allgegenwärtige Sprache
Zusammenarbeit zwischen Entwicklern und Domänenexperten unter Verwendung einer gemeinsamen Sprache, um Missverständnisse zu reduzieren.

2. Begrenzte Kontexte
Klare Trennung verschiedener Teile der Domäne, um Autonomie und Klarheit innerhalb spezifischer Kontexte zu gewährleisten.

3. Entitäten und Wertobjekte

  • Entitäten: Objekte mit einer einzigartigen Identität.
  • Wertobjekte: Unveränderliche Objekte, die durch ihre Attribute definiert werden.

4. Aggregate
Cluster von Domänenobjekten, die bei Datenänderungen als eine Einheit behandelt werden.

5. Repositorys
Abstrahiert die Persistenzlogik und bietet Zugriff auf Aggregate.

6. Domain-Ereignisse
Signale, die ausgegeben werden, wenn bedeutende Aktionen innerhalb der Domain stattfinden.

7. Anwendungsdienste
Kapseln Sie Geschäftsabläufe und Orchestrierungslogik.

Warum TypeScript zu DDD passt

1. Statische Typisierung: Eine starke Typprüfung hilft dabei, die Domänenlogik explizit zu modellieren.
2. Schnittstellen:Verträge zwischen Komponenten durchsetzen.
3. Klassen: Stellen Entitäten, Wertobjekte und Aggregate auf natürliche Weise dar.
4. Typschutz:Gewährleisten Sie die Typsicherheit zur Laufzeit.
5. Dienstprogrammtypen: Aktivieren Sie leistungsstarke Typtransformationen für dynamische Domänen.

Praktische Umsetzung

1. Modellierungseinheiten
Entitäten haben eindeutige Identitäten und kapseln ihr Verhalten.

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. Wertobjekte schaffen
Wertobjekte sind unveränderlich und werden nach Wert verglichen.

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. Aggregate definieren
Aggregate stellen die Datenkonsistenz innerhalb einer Grenze sicher.

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. Repositories implementieren
Repositorys abstrahieren den Datenzugriff.

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. Verwenden von Domänenereignissen
Domänenereignisse benachrichtigen das System über Statusänderungen.

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. Anwendungsdienste
Anwendungsdienste koordinieren Arbeitsabläufe und erzwingen Anwendungsfälle.

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. Arbeiten mit begrenzten Kontexten

Nutzen Sie die modularen Funktionen von TypeScript, um begrenzte Kontexte zu isolieren.

  • Verwenden Sie für jeden Kontext separate Verzeichnisse.
  • Definieren Sie explizit Schnittstellen für die kontextübergreifende Kommunikation.

Beispielstruktur:

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 };
  }
}

Erweiterte Funktionen

Bedingte Typen für flexible Modellierung

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);
  }
}

Vorlagenliteraltypen für die Validierung

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;
  }
}

Meine persönliche Website: https://shafayet.zya.me


Nun, es zeigt, wie aktiv Sie in Git-Toilette sind...

TypeScript for Domain-Driven Design (DDD)


Das Titelbild wurde mit OgImagemaker von

erstellt @eddyvinck .Danke Mann, dass du uns dieses Werkzeug geschenkt hast???...

Das obige ist der detaillierte Inhalt vonTypeScript für Domain-Driven Design (DDD). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn