>웹 프론트엔드 >JS 튜토리얼 >도메인 기반 디자인(DDD)을 위한 TypeScript

도메인 기반 디자인(DDD)을 위한 TypeScript

Patricia Arquette
Patricia Arquette원래의
2024-12-25 17:18:14506검색

도메인 중심 설계(DDD)는 핵심 비즈니스 도메인 및 관련 논리에 초점을 맞춰 복잡한 소프트웨어 시스템을 다루는 강력한 접근 방식입니다. 강력한 타이핑과 현대적인 기능을 갖춘 TypeScript는 DDD 개념을 효과적으로 구현하는 훌륭한 도구입니다. 이 기사에서는 TypeScript와 DDD의 시너지 효과를 살펴보고 디자인과 코드 사이의 격차를 해소할 수 있는 실용적인 통찰력, 전략 및 예제를 제공합니다.

도메인 중심 설계 이해

핵심 개념

1. 유비쿼터스 언어
잘못된 의사소통을 줄이기 위해 공유 언어를 사용하는 개발자와 도메인 전문가 간의 협업.

2. 제한된 컨텍스트
도메인의 여러 부분을 명확하게 분리하여 특정 상황 내에서 자율성과 명확성을 보장합니다.

3. 엔터티 및 값 개체

  • 엔티티: 고유한 정체성을 지닌 객체입니다.
  • 값 객체: 해당 속성으로 정의된 불변 객체입니다.

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.