common-domain-driven-design について
複雑なビジネス領域にドメイン駆動設計を適用するコーディングスキルです。ドメインモデリング、境界づけられたコンテキスト、ビジネスロジックとインフラの分離を案内します。
主な機能
- 01境界づけられたコンテキストの特定
- 02ユビキタス言語の定義
- 03集約と値オブジェクトのモデリング
- 04ドメインイベントとリポジトリの指針
- 05レイヤー分離とコンテキストマッピング
活用シーン
- 01ドメインモデルの設計や再構築
- 02集約、エンティティ、値オブジェクトの定義
- 03ドメインロジックとインフラの分離確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ xip-online-applications/ skills --skill common-domain-driven-designインストール後、Claude Code で/common-domain-driven-designを入力して読み込まれたか確認します。
詳細
共通ドメイン駆動設計
ドメイン駆動設計(DDD)を適用して複雑なビジネスドメインをモデル化し、明確な境界を確立し、意図が伝わる保守しやすいコードを作成します。
使用する場面
次のような場合にこのスキルを使用します。
- ドメインモデルを設計または再構成する
- 新しい境界づけられたコンテキスト、集約、エンティティ、値オブジェクトを作成する
- ドメインイベント、リポジトリ、アプリケーションサービスを実装する
- ドメインロジックとインフラストラクチャが適切に分離されているか、コードをレビューする
- ステークホルダーとユビキタス言語について議論または文書化する
主な概念
ユビキタス言語
- ドメインエキスパートと開発者の間で、共通の用語を一貫して使用します。
- ドメイン層のすべてのクラス、メソッド、変数に、ユビキタス言語の用語を使用します。
- 用語が曖昧な場合は明確にし、合意した定義を文書化します。
境界づけられたコンテキスト
- 境界づけられたコンテキストとは、ドメインモデルが定義され、一貫性を保つ明示的な境界です。
- 同じ用語でも、境界づけられたコンテキストが異なれば意味が異なる場合があります。これは想定されることです。
- 境界づけられたコンテキスト間の通信には、明確に定義されたインターフェース(コンテキストマッピング)を使用します。例として、腐敗防止層、共有カーネル、オープンホストサービス、公開言語があります。
構成要素
| 構成要素 | 目的 |
|---|---|
| エンティティ | 時間が経過しても存続する、一意の識別子を持つオブジェクトです。 |
| 値オブジェクト | 識別子を持たず、その属性のみで定義される不変のオブジェクトです。 |
| 集約 | ルートエンティティを唯一の入口とする、エンティティや値オブジェクトのまとまりです。 |
| 集約ルート | 外部コードが集約を参照する際に使用できる唯一のエンティティです。 |
| ドメインイベント | ドメイン内で発生した重要な出来事を記録したものです。 |
| リポジトリ | 永続化を抽象化し、集約を取得および保存します。 |
| ドメインサービス | エンティティや値オブジェクトに自然に属さない、ステートレスな操作です。 |
| アプリケーションサービス | ユースケースを調整し、ドメインオブジェクトに処理を委譲します。ビジネスロジックは含みません。 |
| ファクトリー | 集約やエンティティの複雑な生成ロジックをカプセル化します。 |
手順
-
境界づけられたコンテキストを特定します。 コードを書く前に、作業対象の境界づけられたコンテキストを判断します。明示的に名前を付けます。まだ存在しない場合は、ユビキタス言語を使って提案します。
-
ユビキタス言語を定義します。 タスクに関連する主要なドメイン用語を列挙します。コード、コメント、コミットメッセージ、ドキュメントで、これらの用語を一貫して使います。
-
集約を慎重にモデル化します。
- 各集約は、できる限り小さくします。不変条件を守るために必要なものだけを含めます。
- 他の集約は、直接のオブジェクト参照ではなく、識別子(ID)で参照します。
- すべての状態変更は、集約ルートを通じて行います。
- 集約内で不変条件を保護し、外部コードによって無効な状態にされないようにします。
-
値オブジェクトを積極的に使用します。
- ドメイン概念には、プリミティブ型より値オブジェクトを優先します(例:
Money、EmailAddress、DateRange)。 - 値オブジェクトは不変です。操作によって新しいインスタンスを返します。
- すべての属性を比較して同値性を判定します。
- ドメイン概念には、プリミティブ型より値オブジェクトを優先します(例:
-
副作用に対してドメインイベントを発行します。
- 集約で意味のある状態変更が完了したら、ドメインイベントを発行します。
- イベント名には、ユビキタス言語を使った過去形を用います(例:
OrderPlaced、PaymentReceived)。 - イベントは最小限にし、コンシューマーが必要とするデータだけを含めます。
- 通知、プロジェクション、連携などの副作用は、集約内ではなくイベントハンドラーで処理します。
-
レイヤーを明確に分離します。
- ドメインレイヤー:エンティティ、値オブジェクト、集約、ドメインイベント、リポジトリインターフェース、ドメインサービス。フレームワークやインフラストラクチャのインポートは含めません。
- アプリケーションレイヤー:アプリケーションサービス(ユースケースのオーケストレーター)、コマンド/クエリハンドラー。ドメインレイヤーのみに依存します。
- インフラストラクチャレイヤー:リポジトリの実装、メッセージングアダプター、ORM マッピング、外部 API クライアント。ドメイン/アプリケーションで定義されたインターフェースを実装します。
- プレゼンテーション/API レイヤー:コントローラー、シリアライザー、DTO。外部入力をアプリケーションコマンド/クエリに変換します。
-
リポジトリのルール
- 集約ルートごとにリポジトリを 1 つ用意します。
- リポジトリインターフェースはドメインレイヤーに、実装はインフラストラクチャレイヤーに配置します。
- リポジトリが受け取り、返すのは集約全体のみです(部分的なエンティティは扱いません)。
-
境界を越える場合はコンテキストマッピングを適用します。
- 腐敗防止層(ACL)を使って、自分たちのモデルと外部/レガシーモデルの間を変換します。
- コンテキスト境界では、明示的な DTO または統合イベントを定義します。内部のドメインオブジェクトを外部に漏らしてはいけません。
- 2 つのチームが小さなモデルを共有する場合は、明確な所有権ルールを定めた共有カーネルを使います。
-
フォルダー/モジュール構成。 境界づけられたコンテキストを反映するようにコードを構成します。
src/ <bounded-context>/ domain/ model/ # Aggregates, entities, value objects event/ # Domain events service/ # Domain services repository/ # Repository interfaces application/ command/ # Command handlers / use cases query/ # Query handlers / read models infrastructure/ persistence/ # Repository implementations messaging/ # Event bus adapters presentation/ # Controllers, DTOs -
設計を検証します。
- 集約ルートのすべての公開メソッドが、ユビキタス言語におけるドメインアクションに対応している必要があります。
- ドメイン用語で名前を付けられないものがあれば、モデルを見直します。
- 次の点を確認します。「この集約は、外部に頼らずに自身の不変条件を守れるか?」できない場合は、境界を引き直します。
- アプリケーションサービス、コントローラー、インフラストラクチャにビジネスロジックが漏れていないことを確認します。
コメント
Markdown 対応