仕様駆動開発SDDでドメインモデルはどこから生まれるか

ドメインモデルは「クラス設計」からは生まれません

仕様駆動開発(SDD)でドメインモデルを作ろうとすると、多くの人が最初にやってしまうのが「クラス図を書くこと」です。しかし実際には、ドメインモデルはクラス設計の結果ではありません。仕様の中にすでに存在している概念を取り出したものがドメインモデルです。

つまり、モデリングとは「設計する行為」ではなく「発見する行為」に近いものです。仕様書の文章、業務フロー、画面の振る舞いの中に、すでにエンティティや値オブジェクトの候補が埋まっています。SDDではそこを読み解くことが開発の出発点になります。

SDD(仕様駆動開発)とは何を駆動するのか

仕様駆動開発は「仕様書を元に実装する開発」ではありません。仕様が、アーキテクチャ・API・モデル設計すべての判断基準になる開発です。重要なのは「どのデータを持つか」ではなく、「どんな振る舞いが保証されるか」です。

例えば次の仕様を見てください。

  • 予約は同じ時間帯に重複してはならない
  • 支払い完了後のみ予約は確定する
  • 確定後の予約は変更できないがキャンセルは可能

この時点で、すでにドメインモデルの骨格は存在しています。テーブル設計を考える前に、「状態」と「制約」が見えてきます。

ドメインモデルの出発点は「状態遷移」

ドメインモデルはデータ構造からではなく、状態の変化から生まれます。仕様を読むとき、最初に探すべきものは名詞ではなく状態の変化です。

仕様から読み取るべき3つの要素

  • 生成条件
  • 変更可能条件
  • 禁止条件

この3つがそろうと、エンティティ候補が現れます。

仕様の記述 モデルでの意味
予約を作成できる 生成条件(コンストラクタ)
支払い後は変更不可 不変条件(インバリアント)
キャンセルは可能 振る舞い(メソッド)

ここで初めて「Reservation」という存在が見えてきます。これはテーブルではなく「ルールのまとまり」です。

具体例:予約システムからドメインモデルを抽出する

画面仕様を元にモデルを考えます。

  • ユーザーが時間枠を選択
  • 仮予約が作成される
  • 決済完了で確定
  • 確定後は日時変更不可

この仕様から、単なるDTOではないモデルが必要になります。

public class Reservation {
    private ReservationStatus status;
    private TimeSlot slot;

    public void confirm(Payment payment) {
        if (!payment.isCompleted()) {
            throw new IllegalStateException("支払い未完了");
        }
        this.status = ReservationStatus.CONFIRMED;
    }

    public void changeSlot(TimeSlot newSlot) {
        if (status == ReservationStatus.CONFIRMED) {
            throw new IllegalStateException("確定後は変更不可");
        }
        this.slot = newSlot;
    }
}

ここで重要なのは、DBのカラムを再現していないことです。仕様のルールをコードで表現したものがドメインモデルです。

値オブジェクトが生まれる瞬間

SDDでは値オブジェクトは自然に現れます。たとえば「時間帯」は単なる文字列ではありません。

  • 09:00〜10:00は1つの意味を持つ
  • 比較できる
  • 重複判定が必要

これはStringではなく概念です。

public record TimeSlot(LocalDateTime start, LocalDateTime end) {
    public boolean overlaps(TimeSlot other) {
        return !start.isAfter(other.end) && !end.isBefore(other.start);
    }
}

この時点で、仕様の「同時間帯の重複禁止」を実装できます。モデルは設計者が考えたのではなく、仕様が要求しています。

よくある失敗:テーブル設計から始める

SDDで最も多い失敗は、先にER図を作ることです。すると次の問題が起きます。

  • ルールがサービス層に分散
  • バリデーション地獄
  • 「更新できてしまう」バグ

特に危険なのが「statusカラムを更新するだけ」の実装です。仕様では「確定後は変更不可」なのに、UPDATE文1つで破壊できます。ドメインモデルを経由しないと、仕様を守れません。

仕様からモデルを抽出する手順

実際には次の順番で行います。

  • 画面仕様を読む
  • 状態が変わる瞬間に印をつける
  • 禁止条件を集める
  • 同じルールを共有する対象をまとめる
  • クラス名を後から付ける

ポイントは名前を最初に決めないことです。名前を先に決めると、実装都合のモデルになります。SDDでは「振る舞いのまとまり」を先に見つけ、その後に名前を与えます。

注意点:仕様が曖昧な場合

仕様駆動開発のリスクは、仕様が曖昧だとモデルも曖昧になることです。

  • 「基本的に変更不可」
  • 「原則キャンセル不可」

この表現はモデル化できません。どこまで禁止か決めないと、不変条件が書けないためです。SDDは仕様の品質に依存します。実装が止まるのは悪いことではなく、仕様の不足が見えたサインです。

ドメインモデルは設計者の発明ではない

ドメインモデルはアーキテクトのセンスで作るものだと思われがちですが、実際は逆です。仕様を丁寧に読むほど、モデルは「勝手に現れます」。

仕様に存在しないモデルを作ると、次の現象が起きます。

  • サービスクラスが肥大化する
  • if文が増える
  • 同じチェックが何度も書かれる

これはモデルが不足している状態です。ルールがコードのあちこちに散らばり、ドメインが表現されていません。

最後に大事なことを一つだけ書きます。SDDにおいてドメインモデルは「最初に考えるもの」ではありません。仕様を読み、振る舞いを抜き出し、不変条件を集めた結果として、後から姿を現します。つまりモデリングとは設計ではなく、理解の深さの副産物です。設計がうまくいかないときは、図を描き直すのではなく、仕様を読み直すほうが近道になることが多いです。