コンテンツにスキップ

Kotlin

starlightBlog.tags.count

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