本文へスキップ
BecomeCoder

C#実践コース · 第7章 DDD ― ドメイン駆動設計 · レッスン42

ドメインの振る舞いとドメインサービス

ブラウザで完結

導入

「データだけのクラス+処理は全部サービス」という構造はドメイン貧血症というアンチパターンです。DDDでは業務ルールを、そのデータを持つ本人(エンティティ・値オブジェクト)に書くのが流儀です。

図解

flowchart TB
    A["❌ 貧血症<br/>呼び出し側が Point>=500 を判断<br/>(ルールが外に漏れる)"]
    B["✅ 本人に聞く<br/>member.CanExchangeGift()<br/>(ルールは本人の中に1か所)"]
    A -->|Tell, Don't Ask| B
    style B fill:#e8f5e9

サンプル

var member = new Member(1, "Taro", point: 480);

// ✅ ルールは本人に聞く(Tell, Don't Ask)
Console.WriteLine(member.CanExchangeGift());   // False
member.AddPoint(50);
Console.WriteLine(member.CanExchangeGift());   // True

class Member
{
    public int Id { get; }
    public string Name { get; }
    public int Point { get; private set; }
    public Member(int id, string name, int point) { Id = id; Name = name; Point = point; }

    public void AddPoint(int amount)
    {
        if (amount <= 0) throw new ArgumentException("ポイントは1以上");
        Point += amount;
    }

    // 「500ポイントで景品交換」という業務ルールが、コードのこの1か所にだけ存在する
    public bool CanExchangeGift() => Point >= 500;
}
  • 業務ルールは、そのデータを持つ本人(エンティティ・値オブジェクト)に書く
  • 呼び出し側に判断ロジックを漏らさない(貧血症を避ける)
  • 複数エンティティにまたがり本人に書けないルールだけドメインサービスに置く(乱用注意)

演習

var ticket = new Ticket(usedCount: 9, limit: 10);
Console.WriteLine(ticket.CanUse());   // True
ticket.Use();
Console.WriteLine(ticket.CanUse());   // False

// TODO: Ticket を定義してください
//       ・CanUse() → 利用回数が上限未満なら true
//       ・Use() → 使えないのに呼ばれたら InvalidOperationException、使えるなら UsedCount を+1
class Ticket
{
    public int UsedCount { get; private set; }
    public int Limit { get; }
    public Ticket(int usedCount, int limit) { UsedCount = usedCount; Limit = limit; }
    ___
}
  • 期待される出力: True False(2行)
ヒント1を見る

public bool CanUse() => UsedCount < Limit;

ヒント2を見る

public void Use() { if (!CanUse()) throw new InvalidOperationException(); UsedCount++; }

まとめ

  • 業務ルールはデータを持つ本人に書く(貧血症を避ける)
  • Tell, Don’t Ask(本人に判断させる)
  • 本人に置けないルールだけドメインサービスへ

次回: モデルを守る構造——レイヤードアーキテクチャです。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。