モデルは「データ構造」ではない

「モデルとは何ですか?」と聞かれたとき、多くの人は次のように答えます。

  • データベースのテーブルに対応するクラス
  • APIレスポンスを受け取るための構造体
  • 画面に表示するためのデータ

これらはすべて、モデルの“使われ方”ではありますが、モデルそのものの役割を表してはいません。

DDDにおけるモデルは、データを運ぶ箱ではなく、判断とルールを守る主体です。もしモデルが値を保持するだけの存在になっているとしたら、その設計はすでにドメインから遠ざかっています。


なぜロジックをモデルに寄せるのか

設計の現場では、次のような判断がよく行われます。

  • ロジックはUseCaseに書く
  • モデルはシンプルなデータクラスにする
  • テストしづらくなったら後で考える

一見すると合理的ですが、この判断を積み重ねると、UseCaseは次第に判断の塊になります。そして、同じような判断が別のUseCaseにも現れ始めます。

モデルにロジックを寄せる理由は、コードをきれいにするためではありません。判断を一箇所に集め、必ず守らせるためです。

モデルが自分自身の状態を知り、その状態に対して許される操作だけを提供する。この形を取ることで、「うっかりルールを破る」余地が大きく減ります。


不変条件という考え方

モデルを理解するうえで、重要な概念に「不変条件(invariant)」があります。

不変条件とは、そのモデルが存在し続ける限り、常に成り立っていなければならないルールのことです。

  • 金額は必ず0以上である
  • 完了済みの注文は変更できない
  • 無効な状態の組み合わせは存在しない

これらは、UseCaseの都合や画面の事情によって変わってよいものではありません。だからこそ、モデル自身が責任を持って守る必要があります。

もし不変条件がUseCaseやUIに散らばっていると、そのモデルは簡単に壊れます。


モデルは「正しい使い方」しか提供しない

良いモデルは、誤った使い方ができないように設計されています。

  • 不正な値では生成できない
  • 不正な状態遷移を表現できない
  • ルールを無視した操作が存在しない

これは、例外を投げるかどうか、という話ではありません。そもそも「できてはいけない操作」をAPIとして提供しない、という姿勢の話です。

この考え方を採用すると、UseCaseは驚くほどシンプルになります。なぜなら、「正しいかどうか」を心配する必要がなくなるからです。


EntityとValue Objectは結果として現れる

ここで初めて、DDDでよく知られている用語が登場します。

EntityとValue Objectは、モデルを設計した“結果”として現れる分類です。最初から分類ありきで考えるものではありません。

  • ライフサイクルを持ち、状態が変化するもの
  • 値として扱われ、置き換え可能なもの

この違いは、IDの有無といった表面的な特徴よりも、「何を守る責務を持っているか」によって決まります。

重要なのは、EntityかValue Objectかを正しく当てることではありません。そのモデルが、守るべき判断とルールを本当に守れているかどうかです。


モデルを薄くすると、設計は長持ちしない

「モデルを薄く保つ」という設計方針が語られることがあります。しかし、その言葉が意味するものを誤解すると、設計は短命になります。

モデルが薄いとは、責務が少ないことではありません。責務が曖昧であることです。

判断を持たないモデルは、どこからでも好き勝手に扱えます。その自由さは、一時的には開発速度を上げますが、変更が始まった瞬間に足を引っ張ります。


この章のまとめ

モデルは、データを表すための存在ではありません。モデルは、ドメインの判断とルールを守るための最後の砦です。

次章では、複数のモデルをどのような単位でまとめ、どこまでを一つのルールとして扱うのか、という問題に進みます。そこで登場するのが「集約」という考え方です。