コンテンツにスキップ

ブログ

DDD のリポジトリ記事を追加しました

DDD における リポジトリ(Repository) について、基本的な役割を整理した解説ドキュメントと、Kotlin での実装例をまとめたドキュメントを追加しました。

リポジトリは、ドメインオブジェクトを保存したり、再び取り出したりするための窓口です。
ドメインモデルを DB や SQL の都合から切り離すうえで重要な部品ですが、DAO との違いや、どこまで検索処理を持たせるべきかで迷いやすい部分でもあります。

これまでの DDD 関連ドキュメントでは、値オブジェクト、エンティティ、ドメインサービスを中心に整理してきました。

一方で、ドメインサービスの記事の中では UserRepositoryPartyRepository のようなリポジトリが登場しています。
そのため、先に「リポジトリとは何か」を独立した記事として整理しておくと、今後の集約やアプリケーションサービスの記事にもつなげやすいと考えました。

今回の記事では、リポジトリを単なる DB アクセス部品としてではなく、ドメインオブジェクトを取得・保存するためのインターフェース として扱う考え方をまとめています。

リポジトリの解説記事では、主に次の内容を扱っています。

  • リポジトリとは何か
  • なぜドメイン層と永続化の詳細を分けるのか
  • DAO とリポジトリの違い
  • DB レコードではなくドメインオブジェクトを返す理由
  • 何でも検索できるクラスにしないための注意点
  • リポジトリを集約ルート単位で扱う考え方

Kotlin での具体的なコード例は、実装例ドキュメントとして分けています。

特に、Repository を findByXxx が増え続ける便利な検索クラスにしないこと、更新に必要な取得と画面表示のための検索を分けて考えることを意識して書いています。

DDD の基本要素から順番に読む場合は、先に エンティティ値オブジェクト を読むと理解しやすいと思います。

そのうえで、ドメインオブジェクトを保存・取得する必要が出てきたときに、今回の リポジトリ の記事を読み、実装の形を確認したくなったら Kotlinでのリポジトリ実装例 に進む流れを想定しています。

また、リポジトリは今後まとめる予定の 集約 とも関係が深い概念です。
今回の記事では集約については軽く触れるだけにし、リポジトリを集約ルート単位で扱う理由は別の記事で改めて整理する予定です。

リポジトリの記事を追加したことで、DDD の基本的な部品として、値オブジェクト、エンティティ、ドメインサービス、リポジトリまでを一通り確認できるようになりました。

次は、これらのドメインオブジェクトをどの範囲でまとめるかを考える 集約 について整理していく予定です。
また、読み取り処理と更新処理を分ける考え方である CQRS についても、リポジトリとは別のテーマとして今後まとめていきます。

ドメインサービスの記事を公開しました

ドメインサービス の記事を公開しました。

DDD を学んでいると、エンティティや値オブジェクトにルールを置く、という考え方は比較的イメージしやすい一方で、「どちらにも自然に置きにくいルール」をどう扱うかで迷いやすいと感じています。

今回の記事では、その置き場所としての ドメインサービス について、基本的な考え方を整理しました。
Kotlin での具体的な実装例は、別のドキュメントとして分けています。

DDD では、ドメインルールをできるだけドメイン層に表現することが大切です。

ただ、実際に実装していると、単一のエンティティや値オブジェクトだけでは判断できないルールが出てきます。
たとえば、ユーザー名がシステム全体で一意かどうか、プレイヤーがすでに別のパーティーに所属していないか、といった判断です。

こうしたルールをアプリケーションサービスに直接書いてしまうと、ユースケースの手順とドメイン上の制約が混ざりやすくなります。
また、同じ判断が複数のユースケースに散らばると、ルールの変更にも弱くなります。

そこで、エンティティや値オブジェクトに自然に置けないドメインルールを、ドメインサービスとして切り出す考え方をまとめました。

ドメインサービスの記事では、主に次の内容を扱っています。

  • ドメインサービスとは何か
  • なぜアプリケーションサービスにルールを書きすぎない方がよいのか
  • 一意性の確認や複数集約をまたぐ判断をどう扱うか
  • ドメインサービスを使うべきケースと避けるべきケース

Kotlin での具体的なコード例は、実装例ドキュメントとして分けています。

特に、ドメインサービスを「便利な共通処理置き場」にしないこと、エンティティ自身が判断できるルールまでサービスへ逃がさないことを意識して書いています。

DDD の基本要素から順番に確認する場合は、先に エンティティ値オブジェクト を読むと理解しやすいと思います。

そのうえで、複数のオブジェクトをまたぐルールや、リポジトリへの問い合わせが必要なドメイン判断が出てきたときに、今回の ドメインサービス の記事を読み、実装の形を確認したくなったら Kotlinでのドメインサービス実装例 に進む流れを想定しています。

また、ドメインサービスの呼び出し漏れを構造的に防ぎたい場合の応用として、ダブルディスパッチ の記事にもつながるようにしました。

DDD の基本的なドメインオブジェクトを整理するときの参考になればうれしいです。

DDD の値オブジェクト・エンティティ記事を追加しました

DDD の基本要素である 値オブジェクトエンティティ について、概念と Kotlin での実装例を整理したドキュメントを追加しました。

DDD を学ぶうえで、値オブジェクトとエンティティの違いは最初につまずきやすいポイントだと感じています。
どちらもドメインオブジェクトですが、同一性の考え方や状態変化の扱い方が異なるため、具体例を交えながら整理しました。

DDD の説明では「エンティティは ID で識別する」「値オブジェクトは値で比較する」といった説明がよく出てきます。
ただ、自分が学び始めたときは、その説明だけでは実際のコードにどう落とし込めばよいのかが少し分かりにくく感じていました。

たとえば、ユーザー名や住所のような情報をただの String として扱うのか、それとも専用の型として扱うのか。
注文やユーザーのように状態が変わるものを data class で表現してよいのか。
こうした判断は、概念を知っているだけでは迷いやすい部分だと思います。

今回追加した記事では、まず値オブジェクトとエンティティの考え方を整理し、そのうえで Kotlin のコード例までつなげる構成にしました。
DDD の用語を覚えるだけでなく、「では実装ではどう書くのか」まで確認できるようにしています。

値オブジェクトの記事では、住所・金額・ユーザー ID のような例を使いながら、値そのものに意味があるデータ をどう表現するかを整理しています。

単に StringInt を使うと、どの値が何を表しているのかがコードから読み取りにくくなります。
値オブジェクトとして専用の型を作ることで、意味を型に閉じ込めたり、不正な値を生成時に防いだりできます。

Kotlin の実装例では、単一プロパティの値オブジェクトと複数プロパティの値オブジェクトを分けて扱っています。
また、@JvmInline value classdata class の違いについても別記事にして、どちらを選ぶべきか判断しやすいようにしました。

エンティティの記事では、ユーザーや注文のように、属性が変わっても同じものとして扱うべきオブジェクト を整理しています。

エンティティで重要なのは、同一性を属性の一致ではなく ID で判断することです。
たとえば住所や名前が変わっても、同じユーザー ID を持っていれば同じユーザーとして扱います。

Kotlin の実装例では、data class の自動生成される equals / hashCode をそのまま使うと、エンティティの同一性とずれてしまう点にも触れています。
状態変更を外部から直接書き換えるのではなく、ドメインメソッドとして表現する書き方もまとめました。

まずは エンティティ値オブジェクト の概念記事を読むのがおすすめです。
この 2 つで「ID で区別するもの」と「値で判断するもの」の違いをつかんでから、Kotlin の実装例に進むと理解しやすいと思います。

その後、値オブジェクトを Kotlin で書くときに @JvmInline value classdata class のどちらを使うか迷ったら、value class と data class の違い を読む流れを想定しています。

今回の記事で、DDD の基本的なドメインオブジェクトである値オブジェクトとエンティティを一通り整理できました。

今後は、集約・リポジトリ・ドメインサービスなど、値オブジェクトやエンティティを組み合わせて設計する部分も少しずつまとめていきたいと考えています。
特に、実装時に「これはエンティティに書くべきか」「ドメインサービスに切り出すべきか」と迷いやすい部分は、自分の理解の整理も兼ねて記事にしていく予定です。

DDD 関連の記事は、引き続き実務で使う知識を自分の言葉で整理しながら充実させていきます。

DDD 入門ドキュメントを作成しました

ドメイン駆動設計(DDD)入門|ドメイン・ドメインオブジェクト・ドメインモデルの違い を公開しました。

DDD を学び始めると「ドメイン」「ドメインオブジェクト」「ドメインモデル」など似た用語が並び、最初の一歩でつまずきやすいと感じていたので、入門用のドキュメントとして整理しました。自分も当初は「ユーザードメイン」「商品ドメイン」のように、エンティティ単位のものをドメインだと勘違いしていたので、そのあたりも含めて、用語の違いがつかみやすい内容にしています。

ネットショップや SNS の具体例を交えながら、次の 4 つを解説しています。

  • ドメイン … アプリが再現しようとしている現実の仕事・ルール
  • ドメインオブジェクト … ドメインの要素をプログラムで表現した部品
  • ドメインモデル … ドメインオブジェクトを組み合わせた全体設計
  • ドメイン駆動設計(DDD) … ルールを一か所にまとめてプログラムを組み立てる考え方

DDD 関連ドキュメントの入口として置いています。気づきやフィードバックがあれば、ぜひ教えてください。

ダブルディスパッチの記事を更新しました

エンティティがドメインサービスに依存!?ダブルディスパッチとは? のドキュメントを、このブランチで大幅に更新しました。

以前は概念の説明が中心だった記事を、図やコード例を交えた「読んで実装に活かせる」内容に仕上げ直しています。

ダブルディスパッチは「一時的な依存の逆転」という性質がポイントですが、文章だけだと通常の依存関係との違いが伝わりにくいと感じていました。

また、どの場面で採用すべきか・避けるべきかの判断基準や、具体的なコードがなかったため、学習用のアウトプットとしてはまだ不十分な状態でした。今回の更新で、そのあたりを補完しています。

次の図を新たに追加しました。

  • ドメイン層の基本的な依存関係 — エンティティ、値オブジェクト、ドメインサービス、リポジトリ IF の関係を一覧化
  • 通常の依存関係とダブルディスパッチ実行時の比較 — コード上の依存と、メソッド呼び出し時だけ生じる一時的な逆転を対比
  • 問題のある実装とダブルディスパッチ実装の呼び出しフローaddMember / joinMember / verifyCanJoin を使った比較図

図が読みやすくなるよう、サイト側でも Mermaid の表示設定(フォントサイズや余白)と CSS を調整しています。

  • 「一時的な依存の逆転」という表現を明確化し、恒久的な設計変更ではないことを強調
  • ルール漏れが起きうる「基本的な依存関係」の問題点を、フローチャートで整理
  • ダブルディスパッチによって「アプリケーションサービスが検証を忘れる」ミスを構造的に防げる理由を追記

新セクション 「ダブルディスパッチを使用すべきケースと避けるべきケース」 を追加しました。

採用を検討したい場面(集約外の情報が必要、ルール漏れが致命的、など)と、無理に使わなくてよい場面(集約内で完結するルール、運用で十分な場合、など)を箇条書きでまとめています。

「パーティーにプレイヤーを参加させる」という例で、次の 2 パターンを Kotlin で掲載しています。

  1. 問題のある実装Party.addMember が public のまま、検証をバイパスできてしまうケース
  2. ダブルディスパッチの実装joinMember 内で PartyMembershipService を呼び、検証を必ず通すケース

ファクトリメソッド create からも同じ joinMember を経由する構成にし、生成・変更の両方でルールが適用されることを示しています。

次のトピックを追記しました。

  • 一時的な依存の逆転であることを忘れない
  • ドメインサービスは状態を持たない
  • 引数として渡すことで意図を明示する
  • DB からの再構築(リハイドレーション)とは別問題

最後に、記事全体のポイントを 3 つに整理したまとめセクションも加えています。

DDD まわりのドキュメントは、引き続き実務で使う知識を自分の言葉で整理しながら充実させていく予定です。

ダブルディスパッチの記事を読んで気づきやフィードバックがあれば、ぜひ教えてください。