貧血ドメインモデル
ドメインオブジェクトがデータのみを持ち、ビジネスロジックが外部のサービスに散在するアンチパターン
DDD品質
貧血ドメインモデルとは
貧血ドメインモデル (Anemic Domain Model) は、Martin Fowler が指摘したアンチパターンで、ドメインオブジェクトがデータ (プロパティ) のみを持ち、ビジネスロジックが外部のサービスクラスに散在する設計である。
貧血 vs リッチドメインモデル
// ❌ 貧血ドメインモデル: Order はデータだけ
class Order {
id: string;
items: OrderItem[];
status: string;
totalAmount: number;
}
// ロジックが外部サービスに散在
class OrderService {
cancel(order: Order) {
if (order.status !== 'pending') throw new Error('Cannot cancel');
order.status = 'cancelled';
order.totalAmount = 0;
}
addItem(order: Order, item: OrderItem) {
order.items.push(item);
order.totalAmount = order.items.reduce((sum, i) => sum + i.price, 0);
}
}
// ✅ リッチドメインモデル: Order がロジックを持つ
class Order {
private constructor(
readonly id: string,
private items: OrderItem[],
private status: OrderStatus,
) {}
cancel() {
if (this.status !== 'pending') throw new Error('Cannot cancel');
this.status = 'cancelled';
}
addItem(item: OrderItem) {
if (this.status !== 'pending') throw new Error('Cannot modify');
this.items.push(item);
}
get totalAmount(): number {
return this.items.reduce((sum, i) => sum + i.price, 0);
}
}
なぜ問題か
| 問題 | 説明 |
|---|---|
| ロジックの散在 | 同じドメインのルールが複数のサービスに分散 |
| 不変条件の破壊 | 外部から直接プロパティを変更でき、不正な状態になる |
| 重複 | 同じバリデーションが複数箇所に書かれる |
| テストの困難さ | サービスの依存関係が複雑 |
なぜ発生するか
- データベースのテーブル構造をそのままクラスにマッピング (ORM の影響)
- 手続き型プログラミングの習慣
- 「モデルはデータ、サービスはロジック」という誤った分離
Lambda での現実的な判断
Lambda の単純な CRUD 関数では、リッチドメインモデルが過剰な場合がある。
| ケース | 推奨 |
|---|---|
| 複雑なビジネスルール | リッチドメインモデル |
| 単純な CRUD | 貧血でも問題ない |
| バリデーションが多い | リッチドメインモデル |
| 1 関数で完結 | 貧血でも問題ない |
基礎から学ぶなら関連書籍が手がかりになる。