貧血ドメインモデル
ドメインオブジェクトがデータのみを持ち、ビジネスロジックが外部のサービスに散在するアンチパターン
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 関数で完結 | 貧血でも問題ない |
基礎から学ぶなら関連書籍が手がかりになる。
この記事は役に立ちましたか?
関連用語
ドメイン駆動設計 (DDD)
ビジネスドメインの知識を中心に据え、ドメインエキスパートと開発者が共通言語で協働しながらソフトウェアを設計する手法
エンティティと値オブジェクト
DDD における 2 つの基本的なドメインモデル要素 - 同一性で区別するエンティティと、値で区別する値オブジェクト
リポジトリパターン (GoF)
データアクセスロジックをカプセル化し、ビジネスロジックからデータストアの詳細を隠蔽するパターン
DNS
ドメイン名を IP アドレスに変換するインターネットの名前解決システム
集約
ドメイン駆動設計において、一貫性を保つべきオブジェクト群をまとめ、単一のルートエンティティ経由でアクセスする設計パターン
Route 53
AWS のマネージド DNS サービスで、ドメイン登録、DNS ルーティング、ヘルスチェックを提供する