コンテンツにスキップ

ドメインサービス

starlightBlog.tags.count

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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