0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【リファクタリング】第29回 貧血ドメインモデル(Anemic Domain Model)

0
Posted at

はじめに

貧血ドメインモデル(Anemic Domain Model) とは、
ドメインオブジェクトが フィールド(データ)だけを持ち、振る舞い(ビジネスロジック)を持たない状態 を指します。

結果として:

  • ロジックは サービス層 に集中
  • エンティティや値オブジェクトは 単なるデータ入れ物 になってしまう
  • オブジェクト指向の利点(カプセル化・一貫性)が失われる

29.1 特徴

  • エンティティ/値オブジェクトが getter/setter しか持たない
  • ビジネスルールはすべて Service クラス に押し込められている
  • 集約の整合性が 呼び出し側任せ になっている
  • 「データベースのテーブル設計」とほぼ同じ構造になっている

29.2 解決手法

  • ドメインモデルに振る舞いを移す(Move Method)
    → データを扱うロジックは、そのデータを持つクラスに閉じ込める
  • 不変条件をクラス内に保持
    → クラス外で制御していた整合性チェックをオブジェクト内に取り込む
  • DDD(ドメイン駆動設計)の導入
    → エンティティ/値オブジェクトにビジネスルールを持たせ、サービスは調整役に留める

29.3 Kotlin 例

Before:貧血ドメインモデル

data class Order(val items: List<Double>, val customerLevel: Int)

class OrderService {
    fun calculateTotal(order: Order): Double {
        val subtotal = order.items.sum()
        return if (order.customerLevel > 3) subtotal * 0.9 else subtotal
    }
}
  • Order はただのデータ入れ物
  • 計算ロジックはすべて OrderService に集中している

After①:ドメインモデルに振る舞いを統合

data class Order(val items: List<Double>, val customerLevel: Int) {
    fun calculateTotal(): Double {
        val subtotal = items.sum()
        return if (customerLevel > 3) subtotal * 0.9 else subtotal
    }
}

Order が「合計計算」という責務を担い、自己完結


After②:値オブジェクトを導入

@JvmInline
value class Money(val value: Double) {
    init { require(value >= 0) { "金額は正数でなければならない" } }
    operator fun plus(other: Money) = Money(this.value + other.value)
    operator fun times(rate: Double) = Money(this.value * rate)
}

data class Order(val items: List<Money>, val customerLevel: Int) {
    fun calculateTotal(): Money {
        val subtotal = items.reduce { acc, m -> acc + m }
        return if (customerLevel > 3) subtotal * 0.9 else subtotal
    }
}

→ 金額の制約を Money に閉じ込め、Order のロジックも安全になる。


29.4 実務での指針

  • Service クラスが太っていないか? を常に確認
  • ロジックは データを持つクラス に置く(データ+振る舞いの一体化)
  • Kotlin では data class + value class を積極的に活用し、
    不変性と意味付けをクラス内に閉じ込める
  • DDD を導入する場合、エンティティ/値オブジェクトにルールを寄せる

まとめ

  • Anemic Domain Model は「オブジェクト指向の形骸化」
  • 解決策は Move Method / Value Object / DDD 実践
  • 基本思想データとロジックは同じ場所にあるべき

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?