AgentSkillsCN

domain-model-extractor

分析现有代码(未以 DDD 为前提),并以伪代码形式提出领域模型(聚合、本地实体、值对象、领域服务)。建议包含属性、方法、不变条件及聚合边界的设计。适用语言:与具体语言无关。触发条件:“从现有代码中提取领域模型”、“希望用 DDD 重新设计这段代码”、“提出领域模型”、“寻找聚合”、“希望迁移到 DDD”、“分析现有代码并建立领域模型”、“将遗留代码迁移到 DDD”等与现有代码分析及领域模型提取相关的请求。

SKILL.md
--- frontmatter
name: domain-model-extractor
description: >-
  既存コード(DDDを前提としていない)を分析し、ドメインモデル(集約、ローカルエンティティ、
  値オブジェクト、ドメインサービス)を擬似コード付きで提案するスキル。プロパティ・メソッド・
  不変条件・集約境界を含む設計を提案する。対象言語: 言語非依存。
  トリガー:「既存コードからドメインモデルを抽出して」「このコードをDDDで再設計したい」
  「ドメインモデルを提案して」「集約を見つけたい」「DDDに移行したい」
  「既存コードを分析してドメインモデルを作って」「レガシーコードをDDDに」
  といった既存コード分析・ドメインモデル抽出リクエストで起動。

Domain Model Extractor

既存の非DDDコードを分析し、DDDのビルディングブロック(集約・ローカルエンティティ・値オブジェクト・ドメインサービス)を擬似コード付きで提案する。

分析ワークフロー

Phase 1: スコープ確認

ユーザーに以下を確認する:

  • 分析対象のディレクトリ/ファイル範囲
  • ドメインの概要(何を扱うシステムか)
  • 特に関心のある領域(あれば)

Phase 2: 既存コードの探索

以下の順序で情報を収集する:

Step 1: 構造の把握

  • ディレクトリ構造を確認し、モジュール/パッケージの全体像を掴む
  • 主要なクラス/構造体/型を一覧化する

Step 2: データ構造の分析

  • DB スキーマ(マイグレーション、ORMモデル等)があれば確認
  • 主要なクラス/構造体のフィールド・プロパティを確認
  • データ間の参照関係(外部キー、IDフィールド等)を把握

Step 3: ビジネスロジックの所在特定

  • バリデーションロジックの散在箇所を特定
  • 条件分岐の多いメソッドを特定(ビジネスルールの候補)
  • 複数エンティティにまたがる操作を特定

Step 4: トランザクション境界の分析

  • 同一トランザクションで更新されるデータ群を特定
  • これが集約境界の最重要手がかりとなる

Phase 3: ドメインモデルの導出

収集した情報を基に、以下の順序でモデルを導出する。 詳細なヒューリスティクスは references/analysis-heuristics.md を参照。

導出順序:

  1. 値オブジェクト - プリミティブ型で表現されたドメイン概念、検証ロジックの散在箇所から
  2. 集約 - トランザクション境界、CRUDの単位、不変条件の範囲から
  3. ローカルエンティティ - 集約内部でのみ意味を持つ、IDを持つオブジェクト
  4. ドメインサービス - 複数集約にまたがる操作、特定オブジェクトに属さないルール

各モデルに含める情報:

  • プロパティ(型情報付き)
  • ファクトリメソッド(生成時の検証)
  • コマンドメソッド(状態変更操作、不変で新インスタンスを返す)
  • クエリメソッド(状態参照操作)
  • 不変条件(集約が常に満たすべきルール)

Phase 4: 提案ドキュメントの出力

references/output-template.md のテンプレートに従い、以下を含むMarkdownドキュメントを生成する:

  1. 概要(分析対象、発見したモデル数)
  2. 集約ごとのセクション(擬似コード付き)
  3. 値オブジェクト一覧(擬似コード付き)
  4. ドメインサービス一覧(擬似コード付き)
  5. 集約関連図(テキストベース)
  6. 既存コードとの対応表

出力例(抜粋)

以下は EC サイトの注文管理コードを分析した場合の出力イメージ(集約1つ分):

pseudo
Aggregate Order {
  id: OrderId
  customerId: CustomerId        // 他集約へのID参照
  items: List<OrderItem>        // ローカルエンティティ
  status: OrderStatus           // 値オブジェクト
  shippingAddress: Address      // 値オブジェクト

  // Invariants: items は1件以上、合計金額は0より大きい

  static create(customerId, firstItem, address): Order
  addItem(item: OrderItem): Order       // returns 新しいインスタンス
  removeItem(itemId: OrderItemId): Order
  submit(): Order                        // status を Submitted に変更
  totalAmount(): Money
}

分類判断基準

判断ポイント集約ローカルエンティティ値オブジェクト
独自のIDを持つかYes(ルートID)Yes(ローカルID)No
単独で取得されるかYesNo(親経由)No
ライフサイクルがあるかYes親に従属なし(不変)
同一性で比較するかYes(ID)Yes(ID)No(値で比較)

注意事項

  • 既存コードの1クラス = 1集約とは限らない。複数クラスが1集約になることも、1クラスが複数に分割されることもある
  • テーブル構造に引きずられすぎない。テーブルは永続化の都合であり、ドメインモデルの構造と一致するとは限らない
  • 過度に細かい値オブジェクトを作りすぎない。ドメインで意味のある単位のみ
  • 集約は小さく保つ(Vernon's Rule)。迷ったら分割する方向で

関連スキル(併読推奨)

このスキルを使用する際は、以下のスキルも併せて参照すること:

  • domain-building-blocks: 抽出対象となるビルディングブロックの定義と設計
  • aggregate-design: 集約境界の特定と設計ルール
  • cqrs-aggregate-modeling: 読み取り責務が混在する集約の分離指針