集約とトランザクション境界の関係を明確化し、複数集約を単一トランザクションに含めるアンチパターンを 検出・是正する。集約は強い整合性境界であり、ユースケースで複数集約を更新する場合は結果整合性を 使うべきという原則を適用する。コードレビュー、ユースケース設計、リファクタリング時に トランザクション境界の問題を検出する場合に使用。 対象言語: 言語非依存(Java, Kotlin, Scala, TypeScript, Go, Rust, Python等すべて)。 トリガー:「複数集約を同じトランザクションで更新している」「ユースケースに@Transactionalがある」 「集約間の整合性をどう取るか」「Sagaパターンを使うべきか」「トランザクション境界の設計」 「1トランザクション1集約」「結果整合性の実装」「集約をまたぐトランザクション」 といったトランザクション境界関連リクエストで起動。
aggregate-design
DDDの集約(Aggregate)設計ルールに基づくコードレビュー・設計支援・リファクタリングを行う。Evans Rules、Vernon's 4 Rules、Design by Contractに基づき、集約の境界定義、不変条件の検証、 不変(Immutable)設計、ID参照、結果整合性、ドメインイベント連携を包括的にガイドする。
以下のいずれかに該当する場合は必ずこのスキルを使用すること: - 集約(Aggregate)の新規設計・実装・リファクタリング(どの言語でも) - 既存の集約やエンティティクラスのDDD観点でのコードレビュー - 集約の境界決定(「AとBは同じ集約にすべきか?」「この集約は大きすぎるか?」) - 集約内の不変条件・整合性境界の設計 - 集約間の連携方式の判断(ドメインイベント、結果整合性、Sagaパターン) - 可変(Mutable)な集約コードを不変(Immutable)設計にリファクタリングする - publicフィールド、直接参照、push/appendなどカプセル化違反の検出・修正 キーワード例:集約、Aggregate、aggregate boundary、集約ルート、AggregateRoot、 エンティティ設計、DDD実装、Vernon Rules、Evans Rules、集約の分割、真の不変条件
含まれるファイル(7)
- SKILL.md11.0 KB
- evals/evals.json4.1 KB
- evals/trigger-eval.json5.2 KB
- references/python.md7.4 KB
- references/rust.md7.9 KB
- references/scala.md4.9 KB
- references/typescript.md6.8 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
集約設計ガイド
DDDにおける集約(Aggregate)設計の原則。
集約とは
集約 = 整合性の境界
- 集約はオブジェクトのグラフであり、そのグラフを一単位として扱う
- 集約内で真の不変条件(常に満たすべき制約)が維持される
- 集約ルート(ルートエンティティ)が集約全体の唯一のエントリーポイント
境界の例:Car集約
┌─────────────────────────────────────┐
│ Car集約 │
│ ┌──────────────────────────────┐ │
│ │ Car (集約ルート) │ │
│ │ - carId: CarId │ │
│ │ - tires: List[Tire] │ │
│ │ - engine: Engine │ │
│ └──────────────────────────────┘ │
│ ↓ 所有 ↓ 所有 │
│ ┌──────────┐ ┌─────────┐ │
│ │ Tire │ × 4 │ Engine │ │
│ │ (VoかEnt)│ │ (VoかEnt)│ │
│ └──────────┘ └─────────┘ │
└─────────────────────────────────────┘
- TireとEngineはCar集約の境界内にある
- 外部からTireやEngineへ直接アクセスは不可
- Carを経由してのみ操作可能
Design by Contract (DbC)
集約は契約に基づいて設計する。
| 契約 | 説明 | 責任 |
|---|---|---|
| 事前条件 (Precondition) | メソッド呼び出し前に満たすべき条件 | 呼び出し側 |
| 事後条件 (Postcondition) | メソッド実行後に満たされる条件 | 実装側 |
| 不変条件 (Invariant) | 常に満たすべき条件 | 実装側 |
詳細な言語別実装パターンは references/typescript.md、references/scala.md、references/rust.md、references/python.md を参照。
基本原則
1. 不変性(Immutability)推奨
現代においては不変(Immutable)を推奨する。特に理由がなければ不変。 状態更新時は既存値を引き継ぎ、変更するフィールドだけを上書きする。 これにより、フィールド追加時の修正漏れを防ぎ、更新意図が明確になる。
| 言語 | 不変更新パターン |
|---|---|
| TypeScript | Props型 + ...this.props スプレッド構文 |
| Scala | case class + copy() |
| Rust | struct + ..self 構造体更新構文 |
| Python | dataclass(frozen=True) + replace() |
2. 強い整合性境界
集約は一つの強い整合性境界。集約内部の状態はすべてその集約の管理下に置く。
3. 他集約への間接参照
集約内部に別の集約の参照を保持しない。他の集約と関連を持つ場合はIDで間接参照する。
4. 完全コンストラクタ
基本コンストラクタですべての状態を初期化する。オーバーロードする場合も必ず基本コンストラクタを利用する補助コンストラクタとして設計する。
5. 防御的コピー
可変オブジェクトを保持する場合、外部に返す際は必ずコピーを返すか不変オブジェクトに変換する。
6. 不変条件の維持
どのような操作をされても不正な状態に陥ってはならない。不変な集約では基本コンストラクタで保護する。
構造原則
7. 集約ルート経由のアクセス
集約内部のエンティティや値オブジェクトへの直接アクセスは、必ず集約ルートを経由する。
8. 1トランザクション = 1集約
単一のトランザクションで複数の集約を更新しない。集約間の整合性は結果整合性で担保する。
9. 集約を小さく保つ
大きすぎる集約は並行性の問題(ロック競合)を引き起こす。真に一貫性が必要な範囲のみを含める。
連携原則
10. ドメインイベントによる連携
集約の状態変更時にドメインイベントを発行し、他の集約や外部システムはそれを購読して反応する。
11. 楽観的ロック(要件がある場合のみ)
並行更新の衝突検出が必要な場合にのみ、バージョン番号を持たせる。要件がなければ不要。
12. 永続化の無知
集約はドメインロジックに集中し、どう保存されるかは関知しない(リポジトリの責務)。
Evans Rules(エリック・エヴァンス)
DDDの原典からの集約ルール。
Evans Rule 1: ルートエンティティの責任
集約ルートだけがリポジトリから直接取得できる。
- 外部オブジェクトは集約内部への参照を持てない
- 集約ルートは内部エンティティの識別子を渡すことができるが、それは一時的な利用に限る
- 集約ルートは集約全体の不変条件を維持する責任を持つ
Evans Rule 2: 内部オブジェクトへのアクセス制限
境界内のエンティティはローカルな識別子を持つ。それは集約内でのみ一意であればよい。
- 外部から直接アクセス不可
- 内部エンティティの識別子はグローバルに一意である必要はない
Vernon's 4 Rules(ヴォーン・ヴァーノン)
「実践ドメイン駆動設計」からの設計ルール。
Rule 1: 真の不変条件を整合性境界に
Design aggregates based on true invariants (整合性境界の中で真の不変条件を担保)
良い例:Order集約
- 注文合計 = 各明細の小計の合計(真の不変条件)
- この計算は即座に正しくなければならない
悪い例:結果整合性で十分な場合
- 在庫数の更新は別の集約で結果整合性
Rule 2: 小さな集約
Keep aggregates small(集約は小さく)
大きな集約の問題:
- ロック競合が増加
- 並行性が低下
- トランザクション失敗率が上昇
❌ Bad: 大きな集約
Product集約
├── productId
├── name
├── backlogItems: List[BacklogItem] ← 数百件になる可能性
└── ...
✅ Good: 分割した集約
Product集約 BacklogItem集約
├── productId ├── backlogItemId
├── name ├── productId(IDで参照)
└── ... └── ...
Rule 3: 他の集約はIDで参照
Reference other aggregates by ID only(他の集約はIDで参照)
前述の「他集約への間接参照」と同じ原則。
Rule 4: 結果整合性
Use eventual consistency outside the boundary(境界外は結果整合性)
- 集約間の整合性はドメインイベント + 結果整合性で担保
- 単一トランザクションで複数集約を更新しない
レビューチェックリスト
既存の集約をレビューする際に使用する。
Design by Contract
- 事前条件が明確に定義されているか(require)
- 事後条件が満たされているか(ensuring)
- 不変条件がコンストラクタ・各操作後に維持されているか
基本原則
- 集約は不変(Immutable)か、または不変にできない明確な理由があるか
- 他の集約への直接参照を保持していないか(IDのみか)
- 完全コンストラクタを提供しているか
- 補助コンストラクタは基本コンストラクタを利用しているか
- 可変オブジェクトを返す場合、防御的コピーをしているか
- 不変条件がコンストラクタで検証されているか
Evans Rules
- 集約ルートのみがリポジトリから直接取得されるか
- 外部から集約内部への直接参照がないか
- 内部エンティティの識別子は一時的な利用に限られているか
Vernon's Rules
- 真の不変条件のみが集約境界内にあるか(過剰な含有がないか)
- 集約は十分に小さいか(数百件のコレクションを含んでいないか)
- 他の集約はIDのみで参照しているか
- 集約間の整合性は結果整合性で担保されているか
構造原則
- 外部からのアクセスは集約ルート経由のみか
- 内部エンティティを直接操作できる穴がないか
- 集約のサイズは適切か(大きすぎないか)
連携原則
- 状態変更時にドメインイベントを発行しているか
- 楽観的ロックの要件がある場合、バージョン番号を実装しているか
- 楽観的ロックの要件がないのに、不要なバージョン番号を持っていないか
- 永続化ロジックが集約内に漏れていないか
関連スキル(併読推奨)
このスキルを使用する際は、以下のスキルも併せて参照すること:
domain-building-blocks: 集約を構成する要素(値オブジェクト、エンティティ、ドメインサービス)の設計aggregate-transaction-boundary: 集約とトランザクション境界の関係(1トランザクション=1集約ルール)cross-aggregate-constraints: 集約間の制約チェックと結果整合性の設計repository-placement: 集約のリポジトリインターフェースの配置場所
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
後方互換性がゴミコードを量産する構造を検出し、互換性を「契約と撤去計画」として管理する ガバナンスを支援するスキル。公開API境界の明確化、非推奨化サイクル(deprecation cycle)の 制度化、互換層の局所化(Adapter/Strangler Fig)、契約テスト(CDC)による互換性検証、 AI生成コードの互換性ゲート設計を含む。コードレビュー、API設計、リファクタリング、 レガシー移行時に互換性起因の技術的負債を防ぐために使用。 対象言語: 言語非依存(Java, TypeScript, Go, Python, Rust等すべて)。 トリガー:「後方互換性を保ちたい」「非推奨APIをどうする」「互換層が増えてきた」 「レガシー移行の戦略」「API設計レビュー」「互換性のためのコードが多い」 「deprecation policyを作りたい」「破壊的変更の管理」といった互換性管理関連リクエストで起動。
getterの濫用を防ぐための命名規約スキル。ドメインモデルでgetterが必要な場合(永続化、JSON変換など)に `breachEncapsulationOf` プレフィックスを付与することで、カプセル化を破っていることを明示する。 これにより、Tell Don't Ask原則の違反を未然に防ぎ、getterの意図しない使用を抑制する。 コードレビュー、新規実装、リファクタリング時にgetter設計が必要な場合に使用。 対象言語: Java, Kotlin, Scala, TypeScript, Python, Go, Rust。 トリガー:「getterの命名規約」「カプセル化を破るgetter」「永続化用のgetter」 「breachEncapsulation」「getterを作りたいが濫用を防ぎたい」といったgetter命名関連リクエストで起動。
クリーンアーキテクチャを採用しているプロジェクト向けの設計・レビュー支援。4層構造(ドメイン層、 ユースケース層、インターフェースアダプタ層、インフラストラクチャ層)に基づく。特にインフラ層は 横断的関心事(ロギング、設定管理)のみ、永続化やRPCはインターフェースアダプタ層に配置すべき という原則を適用する。トリガー:「クリーンアーキテクチャで」「クリーンアーキテクチャに従って」 「クリーンアーキテクチャのレビュー」など、クリーンアーキテクチャを明示的に指定した場合のみ起動。 一般的な「設計レビュー」「アーキテクチャ相談」では起動しない。
CQRS/ESが集約の境界定義とモデリングに与える影響を解説する。CQRSを導入すると集約は コマンド実行に必要な最小限の状態のみ保持すればよくなり、読み取り責務はリードモデルに 委譲できる。大きすぎる集約の軽量化、集約境界の再定義、イベントによる状態管理を支援する。 集約設計、CQRS導入時のモデリング見直し、パフォーマンス問題の解決時に使用。 対象言語: 言語非依存。 トリガー:「CQRSで集約が変わる」「集約が大きすぎる」「集約にメッセージ1000件」 「集約の更新が重い」「CQRS導入で集約を見直す」「集約を軽量化したい」 「集約にクエリ用データが混ざっている」「集約の境界を再定義」 といったCQRS/モデリング関連リクエストで起動。
CQRSの実装においてイベントソーシングが必然的に必要となる理由を論理的に説明する。 C側からQ側へのデータ同期問題(計算された値の同期不可、トリガーの限界、ポーリングの スケーラビリティ問題、ダブルコミット問題)を段階的に分析し、イベントを真のデータソースに する設計への到達過程を示す。CQRS導入検討、アーキテクチャ設計時に使用。 対象言語: 言語非依存。 トリガー:「CQRSにイベントソーシングは必要か」「C側とQ側の同期方法」 「CQRSでモデルを分ける必要はないのか」「リードモデルの更新方法」 「なぜイベントソーシングが必要か」「ダブルコミット問題」「CQRSの同期問題」 「CQRSはESなしでも動くか」といったCQRS/ES必然性関連リクエストで起動。