도메인 중심 설계(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 중국어 웹사이트의 기타 관련 기사를 참조하세요!