はじめに
DDDのDomainServiceについて学びます。
DomainServiceとは
ドメイン駆動設計(DDD)において、特定のエンティティや値オブジェクトに属さないビジネスロジックをカプセル化するために使用される概念です。ドメインサービスは、エンティティや値オブジェクトでは表現しづらい操作やビジネスルールを実現するためのものです。
ドメインサービスの特徴
・エンティティに属さないロジック
複数のエンティティやアグリゲートにまたがるビジネスロジックや、エンティティに直接関連しないロジックを実装します。
・ステートレス:
通常、状態を持ちません。必要なデータはメソッドの引数として渡されます。これにより、再利用性が高まります。
・ビジネスロジックの集中:
ドメインサービスは、ビジネスロジックを一箇所に集中させることで、コードの可読性と保守性を向上させます。
具体例:eコマースシステムにおけるドメインサービス
・シナリオ
顧客がカートに商品を追加する際に、特定の割引を適用するロジックを実装します。この割引ロジックは、カートや商品エンティティに直接関連しないため、ドメインサービスとして実装するのが適しています。
class Product {
constructor(
private id: string,
private price: number
) {}
get productId() {
return this.id;
}
get productPrice() {
return this.price;
}
}
class CartItem {
constructor(
private product: Product,
private quantity: number
) {}
get itemTotal() {
return this.product.productPrice * this.quantity;
}
get details() {
return {
productId: this.product.productId,
quantity: this.quantity,
total: this.itemTotal,
};
}
}
class Cart {
private items: CartItem[] = [];
addItem(product: Product, quantity: number) {
const item = new CartItem(product, quantity);
this.items.push(item);
}
get cartItems() {
return this.items.map(item => item.details);
}
}
class DiscountService {
applyDiscount(cart: Cart, discountRate: number): number {
const totalAmount = cart.cartItems.reduce((total, item) => total + item.total, 0);
return totalAmount * (1 - discountRate);
}
}
const product1 = new Product('product-1', 100);
const product2 = new Product('product-2', 200);
const cart = new Cart();
cart.addItem(product1, 2); // 2 items of product-1
cart.addItem(product2, 1); // 1 item of product-2
const discountService = new DiscountService();
const totalAfterDiscount = discountService.applyDiscount(cart, 0.1); // 10% discount
console.log(cart.cartItems);
// Output: [{ productId: 'product-1', quantity: 2, total: 200 }, { productId: 'product-2', quantity: 1, total: 200 }]
console.log(totalAfterDiscount);
// Output: 360 (10% discount applied to total of 400)
ドメインサービスのポイント
・再利用性: ドメインサービスを使用することで、ビジネスロジックを複数のエンティティから切り離し、再利用しやすくなります。
・シンプルさ: エンティティ自体をシンプルに保ち、エンティティに直接関係しないロジックを別のクラスに切り出すことで、コードの可読性と保守性を高めます。
・一貫性: ビジネスロジックを一箇所に集中させることで、一貫したルールの適用を保証します。