本書は、ドメイン駆動設計(DDD)とは何かを知らない人にとっては、入門として読むことができる一冊です。ただし、設計における「判断」や「責務」という考え方にまだ触れたことがない段階で読むには、少し早い内容でもあります。
本書が焦点を当てているのは、Entity や Value Object といった用語の使い分けそのものではなく、設計判断の背景にある考え方です。
DDDの本を読んでみたものの、「結局DDDとは何なのか」「なぜこれが必要なのか」が腹落ちしなかった人に向けて、DDDの本質に絞って整理しています。
また、モデルやEntityといった言葉の定義を探し回り、はっきりした答えが見つからずにもやっとしてきた人にとっても、本書は一つの視点を与えるはずです。
なぜ「DDDは難しい」と言われるのか DDDは「EntityとValue Objectを作ること」ではない DDDは「Repositoryをinterfaceにすること」ではない DDDはClean Architectureと同義ではない なぜ設計は時間とともに壊れていくのか この本で扱うDDD なぜ「DDDは難しい」と言われるのか 「DDD(ドメイン駆動設計)は難しい」──これは、多くのエンジニアが一度は口にする言葉です。実際、DDDに関する書籍や記事を読み、Entity、Value Object、Aggregate、Repositoryといった用語を学んだにもかかわらず、「で、実際のプロジェクトでどう使えばいいのか分からない」という感覚を抱いた人は少なくないでしょう。
しかし、本当にDDDそのものが難しいのでしょうか。筆者は、DDDが難しく感じられる最大の理由は、「DDDが何であるか」を学ぶ前に、「DDDが何ではないか」を十分に理解しないまま、表面的な技法だけを適用しようとしてしまう点にあると考えています。
DDDは、フレームワークでも、設計パターン集でもありません。ましてや、Entityクラスを作り、Repositoryインターフェースを定義すれば完成するようなチェックリストでもありません。にもかかわらず、多くの現場では、DDDが「特定のクラス構成」や「お作法の集合」として誤解されたまま導入されています。
この章ではまず、DDDを正しく理解するための第一歩として、「DDDとは何ではないのか」を整理します。これはDDDを否定する章ではなく、むしろDDDの本質に近づくために、不要な誤解を一つずつ取り除いていくための章です。
DDDは「EntityとValue Objectを作ること」ではない DDDの説明として、最もよく見かけるのが次のようなものです。
IDを持つものが Entity 値で同一性を判断するものが Value Object これ自体は間違いではありません。しかし、この説明だけを鵜呑みにしてしまうと、「DDD = EntityとValue Objectを定義すること」という非常に危険な短絡に陥ります。
実際の現場では、次のようなコードをよく目にします。
ただのDTOにすぎないEntity ロジックを一切持たないValue Object 生成されるが、ほとんど使われないドメインモデル これらは、DDDの用語を使ってはいるものの、DDDの考え方はほとんど反映されていません。EntityやValue Objectは、DDDの「結果」であって「目的」ではないのです。
DDDが本当に扱おうとしているのは、「そのシステムにおいて、何が重要で、何を守るべきか」という問いです。EntityかValue Objectか、という分類は、その問いに答えた結果として自然に現れるものにすぎません。
DDDは「Repositoryをinterfaceにすること」ではない DDDやClean Architectureの文脈で、Repositoryはしばしば次のように説明されます。
永続化処理を隠蔽する データベースへの依存を排除するためにinterfaceを切る これも一部は正しいのですが、ここでも「手段」が「目的」にすり替わる危険があります。Repositoryをinterfaceにした瞬間にDDDが成立するわけではありません。
Repositoryの本質的な役割は、「ドメインモデルのコレクションとして振る舞う」ことです。データベースの都合ではなく、ドメインの言葉でモデルを取得・保存できるようにする。その結果として、永続化の詳細が隠蔽されるのであって、interfaceを切ること自体が目的ではありません。
もしRepositoryが、単なるCRUD操作のラッパーになっているのであれば、それはDDD的なRepositoryとは言いがたいでしょう。
DDDはClean Architectureと同義ではない DDDの話題になると、必ずと言っていいほどClean ArchitectureやHexagonal Architectureといった言葉が並びます。そのため、「DDD = Clean Architecture」だと誤解されがちです。
しかし、両者は扱っているレイヤーが異なります。DDDは「どうモデルを考えるか」「業務の本質をどうコードに落とすか」という思考法であり、Clean Architectureは「依存関係をどう制御するか」という構造の話です。
両者は相性が良く、組み合わせて使われることも多いですが、同一ではありません。Clean Architectureの形を整えただけでは、ドメインが貧血状態のまま放置されることもあります。
つまり、DDDはアーキテクチャ図を描く前に始まっているのです。
なぜ設計は時間とともに壊れていくのか 多くのプロジェクトでは、開発初期の設計はそれなりに整っています。しかし、機能追加や仕様変更を繰り返すうちに、次第に次のような状態に陥ります。
条件分岐だらけのUseCase あちこちから呼ばれる共通関数 変更の影響範囲が読めないコード これは、エンジニアの技術力不足だけが原因ではありません。設計が「変更に耐えられない形」で作られていた場合、どれだけ注意深く書いても、いずれ破綻します。
DDDは、この「変更によって設計が壊れていく問題」に正面から向き合うための考え方です。どこにルールを閉じ込め、どこを変更可能にするのか。その境界を明確にすることが、DDDの出発点です。
この本で扱うDDD 本書では、DDDを「銀の弾丸」としては扱いません。また、すべてのプロジェクトにDDDを適用すべきだとも主張しません。
本書で扱うDDDは、次の問いに答えるための道具です。
なぜこのロジックはここにあるのか なぜこの変更は怖いのか なぜ設計を説明できないのか これらの問いに向き合う準備ができたとき、DDDは初めて意味を持ちます。
...
ドメインとは「業務ロジック」のことではない なぜ「判断」が重要なのか ドメインは「処理の流れ」を知らない ドメインとUseCaseの違い ドメインは中心に置かれるが、万能ではない ドメインを見失った設計の末路 ドメインとは「業務ロジック」のことではない 「ドメインとは何ですか?」と聞かれたとき、多くの人は次のように答えます。
業務ロジックのこと ビジネスルールのこと これらは、完全に間違いではありません。しかし、この理解のまま設計を進めると、ほぼ確実にドメインは壊れていきます。
なぜなら、「業務ロジック」という言葉はあまりにも広く、そして曖昧だからです。画面遷移の条件も業務ロジックですし、APIを呼ぶタイミングも業務ロジックと言えてしまいます。その結果、「どこまでがドメインなのか」が誰にも説明できなくなります。
本書では、ドメインをもう少し狭く、そして厳密に扱います。
ドメインとは、そのシステムが守るべき判断とルールの集合です。
なぜ「判断」が重要なのか システムが存在する理由は、計算をすることでも、画面を表示することでもありません。多くの場合、システムは「判断」をするために存在しています。
この注文は成立してよいか この支払いは受け付けてよいか この状態で次の操作に進んでよいか これらはすべて判断です。そして、この判断には必ずルールがあります。もしルールがなければ、判断はできません。
DDDが注目するのは、この「判断」と「ルール」がどこに書かれているのか、そしてそれが守られているのか、という点です。
もし判断のロジックが、
UseCaseのあちこちに散らばっていたり UI層に紛れ込んでいたり 仕様書の文章にしか存在していなかったり する場合、そのシステムは非常に壊れやすい状態にあります。
ドメインは「処理の流れ」を知らない ドメインを設計するとき、多くの人が最初につまずくポイントがあります。それは、「ドメインは何を知っていて、何を知らなくてよいのか」という問題です。
結論から言うと、ドメインは処理の流れを知る必要はありません。
どの画面から呼ばれたのか API経由なのか、バッチなのか 同期処理なのか、非同期処理なのか こうした情報は、ドメインにとって本質的ではありません。ドメインが知るべきなのは、「この状態で、この操作は許されるか」という一点だけです。
この切り分けができていないと、ドメインモデルはすぐに肥大化し、テストも困難になります。
ドメインとUseCaseの違い DDDを学び始めると、ドメインとUseCaseの役割の違いが分からなくなることがあります。両方とも「業務の中心」を扱っているように見えるからです。
しかし、両者の責務は明確に異なります。
ドメイン:判断とルールを守る UseCase:処理の流れを組み立てる UseCaseは、「何を」「どの順番で」行うかを知っています。一方で、ドメインは「それをしてよいかどうか」だけを知っています。
もしUseCaseの中に大量のif文が現れ始めたら、それはドメインが本来持つべき判断をUseCaseが肩代わりしているサインです。
ドメインは中心に置かれるが、万能ではない DDDでは、「ドメインを中心に置く」という表現がよく使われます。この言葉は非常に強く、誤解を招きやすい表現でもあります。
ドメインを中心に置くとは、
すべての処理をドメインに書くこと すべてのクラスをドメインから呼ぶこと ではありません。
中心に置くとは、「設計上の判断基準をドメインに寄せる」という意味です。このルールはドメインにあるべきか、それともUseCaseやUIにあってよいのか。その判断を常に意識することが重要です。
ドメインを見失った設計の末路 ドメインが曖昧なまま設計されたシステムでは、次のような現象が起こります。
似たようなチェック処理があちこちに増える 仕様変更のたびに複数箇所を直す必要がある 「なぜこの条件があるのか」を誰も説明できない これは、コードの問題というより、「考え方の問題」です。
DDDは、これらの問題を一気に解決する魔法ではありません。しかし、「どこにルールを書くべきか」という判断軸を与えてくれます。それだけでも、設計の寿命は大きく変わります。
次章では、ドメインをコードとして表現するための最初の道具である「モデル」について掘り下げていきます。
モデルは「データ構造」ではない なぜロジックをモデルに寄せるのか 不変条件という考え方 モデルは「正しい使い方」しか提供しない 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かを正しく当てることではありません。そのモデルが、守るべき判断とルールを本当に守れているかどうかです。
モデルを薄くすると、設計は長持ちしない 「モデルを薄く保つ」という設計方針が語られることがあります。しかし、その言葉が意味するものを誤解すると、設計は短命になります。
モデルが薄いとは、責務が少ないことではありません。責務が曖昧であることです。
判断を持たないモデルは、どこからでも好き勝手に扱えます。その自由さは、一時的には開発速度を上げますが、変更が始まった瞬間に足を引っ張ります。
この章のまとめ モデルは、データを表すための存在ではありません。モデルは、ドメインの判断とルールを守るための最後の砦です。
次章では、複数のモデルをどのような単位でまとめ、どこまでを一つのルールとして扱うのか、という問題に進みます。そこで登場するのが「集約」という考え方です。
ざっくり一言で もう少し噛み砕くと ドメインとは何か モデルとは何か よくある誤解ポイント Evans 本の有名な一文 モデルに種類があるのはなぜか まとめ ざっくり一言で ドメイン (Domain) → 解こうとしている「問題そのもの・業務の世界」
モデル (Model) → そのドメインを理解・表現するために人間が作った「写像(抽象化)」
もう少し噛み砕くと ドメインとは何か ドメインは コードでではありません 。
業務ルール 制約 用語 価値判断 現実世界で「そうなっている理由」 たとえば口座振替アプリなら:
「引き落とし元口座は必ず1つ存在する」 「同じ支払先に対して複数の引き落とし元は持てない」 「金額は0円以上でなければならない」 これ全部 ドメイン。まだコードの話は何も出てきてません。
モデルとは何か モデルは、そのドメインを扱うために作る表現です。
Entity Value Object Aggregate ドメインサービス それらの関係性 例:
data class DirectDebit( val sourceAccountId: AccountId, val destination: Destination, val amount: Money ) これは「口座振替」という現実の概念を、プログラムで扱える形に落としたもの。
これが ドメインモデル。
よくある誤解ポイント 「ドメイン = ドメインモデル」
これは 半分正しくて半分間違い。
ドメイン → 現実世界・業務世界そのもの ドメインモデル → それを 近似 したもの モデルはあくまで「モデル」なので:
...
集約は「クラスの集まり」ではない なぜモデルを「まとめる」必要があるのか 不変条件は「同時性」の問題でもある 集約ルートが持つ責務 集約が大きくなりすぎる理由 集約を分けるという判断 集約は設計を難しくするための概念ではない この章のまとめ 集約は「クラスの集まり」ではない 集約(Aggregate)という言葉を聞いたとき、多くの人は次のようなイメージを持ちます。
EntityとValue Objectを束ねたもの 1対多の関連をまとめた構造 ルートEntityを持つオブジェクトグラフ これらはすべて、集約の“形”を説明してはいますが、集約の本質を説明してはいません。
集約は、構造の話ではありません。集約とは、判断とルールを守るための単位です。どの範囲までを一つの判断として扱い、どこから先は別の判断として切り離すのか。その線引きこそが、集約を設計するという行為です。
なぜモデルを「まとめる」必要があるのか 第3章では、モデルが判断とルールを守る主体であることを説明しました。しかし、実際のシステムでは、判断は一つのモデルだけで完結しないことがほとんどです。
注文は、顧客と商品と支払いに関係する 予約は、日時と空き状況と制約条件に依存する これらをすべて単一のモデルに押し込めると、モデルはすぐに破綻します。逆に、完全に分離してしまうと、「同時に守るべきルール」を保証できなくなります。
集約は、このジレンマに対する答えです。同時に守るべき不変条件を、一つの境界に閉じ込める。それが集約の役割です。
不変条件は「同時性」の問題でもある 集約を理解するうえで、重要なのは「同時性」という視点です。
この操作とあの操作は、同時に行われてよいのか 状態が途中で食い違っても許されるのか 一貫性はどこまで保証されるべきか これらは、ビジネスルールであると同時に、トランザクションの問題でもあります。
集約の境界とは、強い一貫性を保証する範囲です。この範囲の中では、不変条件が必ず守られなければなりません。一方で、境界の外側とは、最終的に整合すればよい、という設計も選択できます。
集約ルートが持つ責務 集約には、必ず外部との窓口となる存在があります。それが集約ルートです。
集約ルートの責務は単純です。
集約内部の不変条件を守る 外部からの操作を一元的に受け付ける 重要なのは、「内部のオブジェクトを直接操作させない」ことです。もし外部から内部のEntityやValue Objectを自由に変更できると、不変条件は簡単に破られてしまいます。
集約ルートは、門番のような存在です。正しい手続きを踏んだ操作だけを通し、それ以外はそもそも表現できないようにします。
集約が大きくなりすぎる理由 集約設計でよくある失敗は、「安全そうだから」という理由で、あらゆるものを一つの集約に詰め込んでしまうことです。
確かに、大きな集約は不変条件を守りやすく見えます。しかし、その代償として次の問題が発生します。
更新のたびに多くのデータを読み込む必要がある ロック範囲が広がり、並行性が下がる 変更の影響範囲が大きくなる 集約を小さくするとは、責任を放棄することではありません。どの一貫性を本当に守る必要があるのかを見極めることです。
集約を分けるという判断 集約を分けるときは、次のような判断基準で考えます。
同時に変更される必要があるか 不整合が一瞬でも許されないか 一つの操作として扱うべきか これらの問いに「はい」と答えられる範囲が、一つの集約になります。
それ以外の関係は、IDによる参照やイベントによる連携で十分な場合がほとんどです。
集約は設計を難しくするための概念ではない 集約は、DDDの中でも特に難解だと感じられがちな概念です。しかし、その理由の多くは、「正解を探そうとする」姿勢にあります。
集約に唯一の正解はありません。重要なのは、その集約が
どの不変条件を守っているのか なぜその境界なのか を説明できることです。
この章のまとめ 集約とは、モデルをまとめるための構造ではありません。集約とは、同時に守るべき判断とルールの単位です。
次章では、この集約をどのように取得し、保存し、システムの外側とつなぐのか。そのための仕組みである「Repository」について掘り下げていきます。
Repositoryは「永続化層」ではない Repositoryが扱うのは「集約」である なぜRepositoryを介さずに触ってはいけないのか interfaceを切ることが目的ではない 良いRepositoryは「問い」を持っている RepositoryとUseCaseの健全な距離 Repositoryを薄くしすぎない この章のまとめ Repositoryは「永続化層」ではない Repositoryと聞いて、多くの人がまず思い浮かべるのは次のような役割です。
データベースへのアクセスを隠蔽する CRUD処理をまとめる ORMやSQLの詳細を隔離する これらはRepositoryの“効果”ではありますが、Repositoryそのものの本質ではありません。
Repositoryは、永続化のための仕組みではなく、集約を扱うための抽象です。もしRepositoryを「DB操作クラス」として設計してしまうと、DDDの重要な意図はほぼ失われます。
Repositoryが扱うのは「集約」である 前章で述べた通り、集約は「同時に守るべき判断とルールの単位」でした。
Repositoryは、その集約を
取得する 保存する ための唯一の窓口です。
重要なのは、Repositoryが
Entity単体 テーブル単位のレコード を扱う存在ではない、という点です。Repositoryが扱うのは、常に意味を持った集約全体です。
この制約があるからこそ、集約の不変条件は安全に保たれます。
なぜRepositoryを介さずに触ってはいけないのか もし集約の内部オブジェクトを、Repositoryを介さずに直接取得・更新できてしまうと、次のような問題が起こります。
不変条件を守らずに状態を書き換えられる 集約の境界が形骸化する 「正しい操作」が分からなくなる これは、設計が破綻する典型的なパターンです。
Repositoryは、「この集約は、こういう単位でしか扱えない」というルールをシステム全体に強制する役割を持っています。
interfaceを切ることが目的ではない Repositoryはしばしばinterfaceとして定義されます。そのため、「Repository = interface」という理解が広まりがちです。
しかし、interfaceを切ること自体は目的ではありません。
Repositoryを抽象として定義する理由は、
集約の概念をインフラから切り離すため モデルの言葉で取得・保存を表現するため です。
もしinterfaceを切ったにもかかわらず、
findById save delete といった汎用的なCRUDメソッドしか存在しないのであれば、そのRepositoryはドメインの言葉をほとんど語っていません。
良いRepositoryは「問い」を持っている 良いRepositoryは、ドメインの問いをそのままメソッドとして表現します。
有効な注文を取得する 処理待ちの支払いを探す 特定の条件を満たす集約を取得する これらは、SQLやクエリの話ではありません。ドメインが何を知りたいかという問いです。
この形でRepositoryを設計すると、UseCaseのコードは自然言語に近づいていきます。
RepositoryとUseCaseの健全な距離 UseCaseは、Repositoryを使って集約を取得し、操作し、結果を保存します。
ここで重要なのは、UseCaseが
集約の内部構造を知らない 永続化の詳細を知らない という状態を保つことです。
UseCaseが知っているのは、
どの集約を使うか どの操作を呼ぶか 結果を保存する必要があるか だけで十分です。
この距離感が保たれていると、UseCaseは「流れ」に集中でき、判断はモデルと集約に委ねられます。
Repositoryを薄くしすぎない 「Repositoryは薄く保つべきだ」という言葉も、誤解されやすい表現です。
Repositoryが薄いとは、ロジックを持たないことではありません。 ドメインの「判断」を持たないことです。
...
設計が壊れる瞬間は、コードが間違ったときではない 「正しい設計」を目指すと、なぜ苦しくなるのか DDDが一貫して嫌うもの 設計の良し悪しは「変更点の数」で測れる DDDは「慎重な楽観主義」である この章のまとめ この章では、DDDを「理論」や「美しさ」から一段引き離し、なぜDDDが現場で効くのかという実践的な理由を掘り下げます。
結論を先に言うと、DDDは「正しい設計」を目指すものではありません。DDDが本当に守ろうとしているのは、設計の耐久性です。
設計が壊れる瞬間は、コードが間違ったときではない 多くのエンジニアは、設計が壊れる原因を次のように考えがちです。
初期設計が甘かった スキルの低いメンバーが実装した リファクタリングを怠った しかし、実務で設計が壊れる瞬間を振り返ると、少し違う景色が見えてきます。
設計が壊れるのは、仕様が変わったときです。
似ているけど微妙に違うルールが追加される 例外ケースが後から増える 「当初は想定していなかった」使われ方をされる コードが間違っていたからではありません。 コードが、変化に耐えられなかっただけです。
DDDは、この「変化」を前提に設計を組み立てます。
「正しい設計」を目指すと、なぜ苦しくなるのか 設計の議論でよく聞く言葉があります。
この設計はDDDとして正しいのか? この責務分割はCleanなのか? 原則に反していないか? こうした問いは一見まっとうですが、DDDの本質からは少しズレています。
なぜなら、DDDにおいて重要なのは
この設計が、将来の変更にどれだけ耐えられるか
だからです。
設計原則は「守るべきルール」ではありません。 設計が壊れにくくなる方向性を示すヒントに過ぎません。
DDDは、正解集を与えてくれません。 代わりに、「どこが壊れやすいか」を教えてくれます。
DDDが一貫して嫌うもの ここまでの章を通して、DDDが何度も避けようとしているものがありました。
それは、
意味の混線
です。
ドメインの意味と技術の都合が混ざる 複数のユースケースの意図が1つのクラスに混ざる 今の仕様と将来の仕様が同じif文に混ざる 混ざった瞬間、そのコードは「今は動くが、未来に弱い」状態になります。
DDDは、
レイヤーを分け 境界を引き 言葉を揃え 意味が混ざらないよう、徹底的に抵抗します。
それは美学ではありません。 変更時に壊れないための実務的な戦略です。
設計の良し悪しは「変更点の数」で測れる DDD的な設計かどうかを見分ける、非常にシンプルな指標があります。
仕様変更が入ったとき、直す場所はいくつあるか
1か所で済むなら、良い設計 あちこちに影響が飛ぶなら、壊れやすい設計 DDDは、この「影響範囲」を小さくするための道具箱です。
Entityに閉じた変更 UseCaseだけで完結する変更 Repositoryの差し替えで済む変更 こうした変更の仕方ができる設計は、結果として長生きします。
DDDは「慎重な楽観主義」である DDDは、未来を完全に予測しようとはしません。
どう変わるかは分からない でも、必ず変わる この前提に立っています。
だからDDDは、
早すぎる抽象化を避け 今わかっている境界だけを引き 変わり始めたところからモデルを育てる という、慎重な姿勢を取ります。
...
パターン1:UseCase境界を「変更の防波堤」にする パターン2:モデルは「ルールが見え始めたところ」から導入する パターン3:Repositoryは「差し替えたい未来」が見えたら切る パターン4:DTO変換が苦しくなったら、境界が間違っている パターン5:「全部DDDにしない」という選択 この章のまとめ この章では、DDDを「思想」や「設計論」から一歩進めて、実務でどのように導入していくかを具体的なパターンとして整理します。
ここで扱うのは、理想形のDDDではありません。
時間が足りない メンバー全員がDDDを理解しているわけではない 既存コードがすでに存在する そうした前提の中で、それでもDDDの考え方がどう役に立つのかを見ていきます。
パターン1:UseCase境界を「変更の防波堤」にする 最も多くの現場で使われているDDDの形は、これです。
Domainは完全ではない Entityも最小限 それでもUseCaseだけは守る UseCaseを「やりたいこと単位」で切り、
入力 処理の流れ Repository呼び出し を閉じ込めます。
このとき重要なのは、UseCaseを肥大化させないことではありません。 重要なのは、
変更理由が1つに収まっているか
です。
多少長くても、
このUseCaseは「〇〇を登録する」 このUseCaseは「〇〇を取り消す」 と説明できるなら、それは実務的には十分にDDDです。
パターン2:モデルは「ルールが見え始めたところ」から導入する 実務でよくある失敗は、最初からモデル(EntityやValue Object)を作り込みすぎることです。
状態遷移を全部入れる 不変条件を先回りして書く 将来使いそうなメソッドを生やす 結果として、
使われないロジック 想定外の変更 消せない責務 がモデルに溜まっていきます。
実務では、
ルールが増え始めてからEntityを育てる
くらいが、ちょうど良いです。
最初は単なるdata classでも構いません。この段階で、それがEntityなのかValue Objectなのかを厳密に判断する必要はありません。 「これはモデルに閉じ込めた方が安全だ」と感じた瞬間が、導入タイミングです。
パターン3:Repositoryは「差し替えたい未来」が見えたら切る 理想論では、Repositoryは最初から切ります。
しかし実務では、
当分DBは変わらない APIも1種類しかない というケースも多い。
その場合、無理にRepositoryを作る必要はありません。
ただし、次の兆候が見えたら要注意です。
テストでモックしたくなった キャッシュを挟みたくなった 取得方法の都合がUseCaseに漏れ始めた この瞬間が、Repositoryを切るサインです。
DDDは「最初から全部やる」設計ではなく、 分離が必要になった瞬間を逃さない設計です。
パターン4:DTO変換が苦しくなったら、境界が間違っている 実務でDDDがつらくなるポイントの1つが、変換地獄です。
API DTO → Domain → UI Model Entity → Domain → UseCase Input 変換コードが増えすぎると、
...
ルールとは「毎回同じ判断が必要になるもの」 UseCaseに書いてよいロジックとの違い UseCaseに置くべき判断 モデルに置くべきルール 「if文がある=ルール」ではない ルールを閉じ込めると、何が楽になるのか ルールは「増え始めたら」閉じ込める この章のまとめ ここまでの章で、何度も「ルールをモデルに閉じ込める」という表現が出てきました。
しかし実務では、ここが一番あいまいになりやすい部分でもあります。
何をルールと呼ぶのか どこまでをモデルに入れるべきなのか UseCaseに書くのと何が違うのか この章では、それらを設計判断ができるレベルまで言語化します。
ルールとは「毎回同じ判断が必要になるもの」 DDDにおけるルールとは、難しいビジネスロジックのことではありません。
もっと単純で、もっと地味なものです。
どのユースケースから呼ばれても、同じ条件・同じ結果になる判断
これが、モデルに閉じ込めるべきルールです。
例えば:
金額は0円以上でなければならない 解約済みの契約は再度有効化できない この状態では次の操作に進めない これらは、
どの画面から操作しても どのAPI経由で呼ばれても 常に守られていてほしい制約です。
だからこそ、UseCaseに散らすのではなく、モデルに集約します。
UseCaseに書いてよいロジックとの違い ここで、よくある疑問が出てきます。
この判断、UseCaseに書いてもよくない?
答えは、場合によるです。
判断の軸はシンプルです。
その判断は「やりたいこと」に依存しているか それとも「概念そのもの」に依存しているか UseCaseに置くべき判断 A画面から来たときだけ許可する 今回は管理者なので例外的に通す この処理では警告だけ出して続行する これらは、
ユースケースの文脈に依存する判断
です。
モデルに入れると、かえって再利用性と可読性が下がります。
モデルに置くべきルール 一方で、
状態として成立しているか 概念として矛盾していないか を判断するロジックは、モデルの責務です。
このルールは、
呼び出し元を知る必要がない 例外を作る理由がない という特徴を持ちます。
「if文がある=ルール」ではない ここで注意が必要です。
モデルに if 文が増え始めると、
これは正しいDDDなのか? モデルが太りすぎていないか? と不安になることがあります。
しかし、
if 文があること自体は問題ではありません。
問題なのは、その if 文が
ユースケースの都合 一時的な仕様 呼び出し元の事情 を知り始めたときです。
...
失敗パターン1:最初から「正しいDDD」をやろうとする 失敗パターン2:用語を揃えずにコードだけDDDにする 失敗パターン3:UseCaseを薄くしすぎる 失敗パターン4:モデルにルールを閉じ込めすぎる 失敗パターン5:DDDを全体に一律適用する 失敗パターン6:「DDDっぽさ」を評価軸にする この章のまとめ ここまでの章で、DDDが「何を大切にしているか」はかなり明確になってきたはずです。
それでもなお、DDDは失敗します。 しかもその多くは、
理解不足 技術力不足 ではありません。
「DDDをやろうとしたから」失敗する
この章では、実務で本当によく見かけるDDDの失敗パターンを整理します。
失敗例を知ることは、 正解例を増やすよりも、ずっと設計判断の精度を上げてくれます。
失敗パターン1:最初から「正しいDDD」をやろうとする 最も多く、最も破壊力のある失敗です。
Entity / Value Object を最初から作り込む Aggregate を先に決める Repository をすべて interface に切る 設計としては「教科書的」に見えます。
しかし実務では、
ルールがまだ分からない 境界もまだ曖昧 将来の変更も見えていない この状態で完成形を目指すと、
想定外の変更に耐えられない設計
が出来上がります。
DDDは「完成形から逆算する設計」ではありません。 変化し始めたところに追従する設計です。
失敗パターン2:用語を揃えずにコードだけDDDにする DDDの中核にあるのは、コードではなく言葉です。
それにもかかわらず、
クラス名だけDDDっぽい メソッド名がユースケースとズレている チーム内で同じ概念を別の言葉で呼んでいる こうした状態でDDDを導入すると、
見た目はDDD、中身はカオス
になります。
言葉が揃っていない設計は、
レビューで揉める 仕様変更の議論が噛み合わない という形で、確実に破綻します。
失敗パターン3:UseCaseを薄くしすぎる 「UseCaseは薄くあるべき」という言葉が、 誤って伝わると起きる失敗です。
UseCaseがRepositoryを呼ぶだけ 判断はすべてモデルに押し込む 一見きれいですが、
処理の流れが追えない どこで何が起きているか分からない 状態になります。
UseCaseは、
やりたいことの物語を書く場所
です。
薄くする対象は「責務」であって、 存在感そのものではありません。
失敗パターン4:モデルにルールを閉じ込めすぎる 第8章で述べた内容の裏返しです。
画面固有の判断 一時的な仕様 今回だけの例外 こうしたロジックまでモデルに入れると、
...
DDDは「分かった瞬間」より「ズレに気づく瞬間」に伸びる 本を読むときは「答え」を探さない 現場は「DDDの教材」である チームでDDDを使うときの距離感 「困りごとを一緒に解決するための共通言語」とは DDDから離れる勇気も必要 この章のまとめ ここまで読み進めてきた読者は、もう気づいているはずです。
DDDは、
手法ではない フレームワークでもない チェックリストでもない ということに。
DDDは、設計との向き合い方そのものです。
だからこそ、
一度学んで終わり
という関係にはなりません。
この章では、DDDを
どう学び続けるのか どう距離感を保つのか どうやって実務に持ち込むのか という、長期的な付き合い方を整理します。
DDDは「分かった瞬間」より「ズレに気づく瞬間」に伸びる DDDを学び始めたとき、多くの人は
EntityとValue Objectの違い Repositoryの役割 Aggregateの境界 といった概念理解に力を注ぎます。
それ自体は必要です。
しかし、本当に設計力が伸びるのは、
「あれ?この設計、どこかおかしいぞ」
と感じた瞬間です。
修正が怖い 変更の影響範囲が読めない なぜこうなっているのか説明できない こうした違和感を
見過ごさない 言語化しようとする ことが、DDDの学習そのものです。
本を読むときは「答え」を探さない DDD関連の書籍や記事は、
厳密で 理論的で 完成度が高い ものが多くあります。
しかし、それを
そのまま現場に当てはめよう
とすると、ほぼ確実に失敗します。
本から得るべきなのは、
設計の完成形 ではなく、
なぜその判断に至ったのか
という思考の軌跡です。
設計書ではなく、 設計者の思考を読む。
これがDDD本との正しい付き合い方です。
現場は「DDDの教材」である DDDを学ぶ上で、 最も優れた教材は何か。
それは、
自分たちのコード
です。
なぜこのクラスは太ったのか なぜこのUseCaseは触りにくいのか なぜこの変更はこんなに大変だったのか これらはすべて、
境界の引き方 ルールの置き場所 言葉の選び方 についてのヒントを含んでいます。
...
DDDは「正解集」ではなかった モデルとは「ルールを閉じ込める場所」だった Repositoryは「境界の翻訳者」だった DDDは「チームで悩むための言語」だった DDDは「使わない判断」も含んでいる おわりに この本を通して、DDDについて多くの言葉が登場しました。
Entity Value Object Repository UseCase 境界 モデル 共通言語 しかし、ここで一度立ち止まって確認したいことがあります。
この本が本当に伝えたかったのは、
これらの言葉そのものではない
という点です。
最終章では、これまでの内容を振り返りながら、 DDDとどう付き合っていくとよいのかを、改めて整理します。
DDDは「正解集」ではなかった 第一章から一貫して述べてきた通り、DDDは
正解を与える設計論 そのまま使える設計テンプレート ではありません。
むしろDDDは、
設計について考え続けるための視点の集合
です。
なぜここに置いたのか なぜこの責務は重く感じるのか なぜこの変更が怖いのか そうした問いを、 曖昧な感覚のままにせず、 言葉として扱えるようにする。
それがDDDの本質でした。
モデルとは「ルールを閉じ込める場所」だった 本書では何度も、
EntityかVOか どこからモデルにするか という話題が出てきました。
しかし、結論はとてもシンプルです。
モデルとは、守りたいルールを閉じ込めるための器
です。
変わってほしくない振る舞い 破られると困る制約 他の場所に漏れると危険な判断 それらが見え始めたとき、 初めてモデルは意味を持ちます。
最初から立派なモデルを作る必要はありません。
必要になったときに、必要な分だけ導入する
それが、実務に耐えるDDDでした。
Repositoryは「境界の翻訳者」だった Repositoryについても、
薄くするべきか ロジックを持つべきか といった議論を扱いました。
ここで大切なのは、
Repositoryはドメインと技術の境界に立つ存在
だという点です。
UseCase側にはドメインの言葉がある 実装側にはDBやAPIの都合がある Repositoryは、その両者を
お互いに汚染させないための翻訳層
として機能します。
「薄い/厚い」という言葉に引きずられるよりも、
どこまでをドメインとして守りたいか どこからを技術的詳細として隔離したいか を考えることの方が重要でした。
DDDは「チームで悩むための言語」だった DDDは、 個人の設計力を高めるだけのものではありません。
...