- モデルは「データ構造」ではない
- なぜロジックをモデルに寄せるのか
- 不変条件という考え方
- モデルは「正しい使い方」しか提供しない
- EntityとValue Objectは結果として現れる
- モデルを薄くすると、設計は長持ちしない
- この章のまとめ
モデルは「データ構造」ではない
「モデルとは何ですか?」と聞かれたとき、多くの人は次のように答えます。
- データベースのテーブルに対応するクラス
- APIレスポンスを受け取るための構造体
- 画面に表示するためのデータ
これらはすべて、モデルの“使われ方”ではありますが、モデルそのものの役割を表してはいません。
DDDにおけるモデルは、データを運ぶ箱ではなく、判断とルールを守る主体です。もしモデルが値を保持するだけの存在になっているとしたら、その設計はすでにドメインから遠ざかっています。
なぜロジックをモデルに寄せるのか
設計の現場では、次のような判断がよく行われます。
- ロジックはUseCaseに書く
- モデルはシンプルなデータクラスにする
- テストしづらくなったら後で考える
一見すると合理的ですが、この判断を積み重ねると、UseCaseは次第に判断の塊になります。そして、同じような判断が別のUseCaseにも現れ始めます。
モデルにロジックを寄せる理由は、コードをきれいにするためではありません。判断を一箇所に集め、必ず守らせるためです。
モデルが自分自身の状態を知り、その状態に対して許される操作だけを提供する。この形を取ることで、「うっかりルールを破る」余地が大きく減ります。
不変条件という考え方
モデルを理解するうえで、重要な概念に「不変条件(invariant)」があります。
不変条件とは、そのモデルが存在し続ける限り、常に成り立っていなければならないルールのことです。
- 金額は必ず0以上である
- 完了済みの注文は変更できない
- 無効な状態の組み合わせは存在しない
これらは、UseCaseの都合や画面の事情によって変わってよいものではありません。だからこそ、モデル自身が責任を持って守る必要があります。
もし不変条件がUseCaseやUIに散らばっていると、そのモデルは簡単に壊れます。
モデルは「正しい使い方」しか提供しない
良いモデルは、誤った使い方ができないように設計されています。
- 不正な値では生成できない
- 不正な状態遷移を表現できない
- ルールを無視した操作が存在しない
これは、例外を投げるかどうか、という話ではありません。そもそも「できてはいけない操作」をAPIとして提供しない、という姿勢の話です。
この考え方を採用すると、UseCaseは驚くほどシンプルになります。なぜなら、「正しいかどうか」を心配する必要がなくなるからです。
EntityとValue Objectは結果として現れる
ここで初めて、DDDでよく知られている用語が登場します。
EntityとValue Objectは、モデルを設計した“結果”として現れる分類です。最初から分類ありきで考えるものではありません。
- ライフサイクルを持ち、状態が変化するもの
- 値として扱われ、置き換え可能なもの
この違いは、IDの有無といった表面的な特徴よりも、「何を守る責務を持っているか」によって決まります。
重要なのは、EntityかValue Objectかを正しく当てることではありません。そのモデルが、守るべき判断とルールを本当に守れているかどうかです。
モデルを薄くすると、設計は長持ちしない
「モデルを薄く保つ」という設計方針が語られることがあります。しかし、その言葉が意味するものを誤解すると、設計は短命になります。
モデルが薄いとは、責務が少ないことではありません。責務が曖昧であることです。
判断を持たないモデルは、どこからでも好き勝手に扱えます。その自由さは、一時的には開発速度を上げますが、変更が始まった瞬間に足を引っ張ります。
この章のまとめ
モデルは、データを表すための存在ではありません。モデルは、ドメインの判断とルールを守るための最後の砦です。
次章では、複数のモデルをどのような単位でまとめ、どこまでを一つのルールとして扱うのか、という問題に進みます。そこで登場するのが「集約」という考え方です。