導入
ドメイン駆動設計(DDD)は、複雑なビジネスロジックを扱う際に非常に有効なアプローチです。しかし、実務においては、設計の意図を誤解したり、誤った実装をすることがよくあります。本記事では、C#を用いてドメイン駆動設計における一般的なアンチパターンを取り上げ、具体的なコード例を通して問題点を明らかにし、改善策を提案します。
教科書レベルの解説(ドメイン駆動設計)
重要な概念の整理
ドメイン駆動設計は、ビジネスの要件をモデル化し、ソフトウェアの設計をそのモデルに基づいて行う手法です。これには、エンティティ、バリューオブジェクト、アグリゲート、リポジトリなどの概念が含まれます。これらの要素を正しく理解し、活用することが成功のカギとなります。
コード例(C#)
public class Order
{
public int Id { get; private set; }
public List Items { get; private set; }
public Order(int id)
{
Id = id;
Items = new List();
}
public void AddItem(OrderItem item)
{
Items.Add(item);
}
public decimal GetTotal()
{
return Items.Sum(item => item.Price);
}
}
public class OrderItem
{
public string ProductName { get; private set; }
public decimal Price { get; private set; }
public OrderItem(string productName, decimal price)
{
ProductName = productName;
Price = price;
}
}
コードの行ごとの解説
- Orderクラスは、注文を表すエンティティとして機能します。
- Itemsプロパティは、OrderItemのリストを保持し、注文の内容を管理します。
- AddItemメソッドは、注文にアイテムを追加するための機能を提供します。
- GetTotalメソッドは、注文の合計金額を計算します。
- OrderItemクラスは、注文に含まれる各アイテムの詳細を保持します。
アンチパターン編
ドメイン駆動設計において、よく見られるアンチパターンの一つは、ビジネスロジックをエンティティのメソッドに埋め込むことです。上記のコードでは、GetTotalメソッドが具体的な計算ロジックを持っていますが、これをリポジトリやサービスに移動させることで、より柔軟でテスト可能な設計に改善できます。
例えば、合計金額を計算するロジックを別のクラスに分離することで、異なる計算方法に対応しやすくなります。
public class OrderService
{
public decimal CalculateTotal(Order order)
{
return order.Items.Sum(item => item.Price);
}
}
このように、ビジネスロジックをサービスクラスに移行することで、Orderエンティティはデータの保持に専念でき、責任が明確化されます。
まとめ
- ドメイン駆動設計では、エンティティに過剰なビジネスロジックを持たせることは避けるべきです。
- ロジックはサービスクラスに分離し、責任を明確にすることで、設計が柔軟で拡張性のあるものになります。