- Home
- Skills
- APIs & Backend
- Domain-Driven Design Aggregate Contract Design
Domain-Driven Design Aggregate Contract Design
Designs DDD aggregate boundaries: aggregate roots, encapsulation, transaction invariants, and domain event contracts.
$5
Works with the AI tools you already use
Domain-Driven Design Aggregate Contract Design
Example session with this skill installed
Design Domain-Driven Design (DDD) aggregate boundary and invariants for our Commercial Loan Disbursement Aggregate.
- Subject: loan-disbursement-aggregate v2.0 (run-id: banking-aggregate-001) managing multi-tranche commercial credit facilities up to $50M.
- Deciders: Lead Domain Architect (David O'Reilly) and Head of Commercial Credit (Elena Rostova).
- Invariants & Scale: Enforcing credit limit caps, interest rate margin floors (>= 2.50%), tranche sequencing, and zero over-allocation across 35 tranches.
- Tension: Developers proposed a mega-aggregate merging BorrowerAccount, LoanFacility, DisbursementTranche, and CollateralPledge into one transaction; David O'Reilly and Elena Rostova strictly reject mega-aggregates, citing incident DB-4910 where concurrent tranche approvals caused 45-minute lock cascades. They mandate: LoanDisbursement as the isolated Aggregate Root, explicit Value Objects (Money, Margin, Schedule), reference-by-identity, single-transaction consistency, and outbox domain events.
Write the aggregate contract specification under docs/.
- Read your context and instructions
- Compiled the domain-driven design aggregate
Wrote docs/architecture/tasks/banking-aggregate-001/aggregate-design/aggregate-contract-spec.md. Complete Domain-Driven Design aggregate specification establishing LoanDisbursement aggregate root boundaries, Value Object encapsulation, reference-by-identity rules, and transactional event outbox contracts.
skill: aggregate-design
DDD Aggregate Contract Specification: Commercial Loan Disbursement [AGG-LOAN-001]
Summary
This specification establishes the Domain-Driven Design (DDD) aggregate boundaries, invariant enforcement rules, entity lifecycle contracts, and domain event outbox specifications for loan-disbursement-aggregate v2.0 under run ID banking-aggregate-001. It governs commercial credit facilities up to $50M executing across multi-tranche disbursement schedules. It decisively eliminates the database lock contention and transaction failures demonstrated in incident DB-4910 (where a monolithic mega-aggregate spanning borrower, collateral, and loan entities induced massive database deadlocks, freezing commercial credit operations for 45 minutes). The architecture establishes LoanDisbursement as an isolated
Aggregate Root, models immutable concepts as
Value Objects (Money, InterestRateMargin, DisbursementSchedule), enforces strict
reference-by-identity for external entities (BorrowerId, CollateralId), restricts database ACID transactions to a single aggregate boundary, and emits transactional outbox domain events (TrancheDisbursedEvent).
Detailed Description
In complex financial domains, developers frequently design "mega-aggregates" that cluster disparate business concepts into a single entity hierarchy. When an aggregate boundary grows too large, multiple concurrent business operations (such as collateral revaluations and tranche disbursements) contend for the exact same database row lock, inducing thread starvation and deadlocks. A formal DDD aggregate boundary defines an atomic consistency island: internal entities are encapsulated behind the Aggregate Root, external aggregates are referenced exclusively by immutable identity keys, and inter-aggregate synchronization is achieved asynchronously via domain events.
Commercial Credit Operation (Disburse Tranche $2.5M)
│
▼
[ LoanDisbursement Aggregate Root (Boundary Boundary) ]
├── 1. Internal Encapsulated Entities:
│ └── `DisbursementTranche` (Entity: ID, Amount, Status)
├── 2. Immutable Value Objects:
│ ├── `Money` (Currency, Decimal Value, Rounding Mode)
│ ├── `InterestRateMargin` (Spread Floor: >= 2.50%)
│ └── `DisbursementState` (Enum: PENDING, DISBURSED, CANCELLED)
├── 3. External References by Identity (Zero Direct Object Graphs):
│ ├── `BorrowerId`: "borr_corp_8812"
│ └── `CollateralPledgeId`: "collat_pledge_9941"
└── 4. Invariant Assertion Gate:
Asserts: Sum(Tranches) <= CreditFacilityCap ($50M)
│
┌───────────────────┴───────────────────┐
▼ (Atomic Commit: Single Aggregate) ▼ (Invariant Breach: Over-Allocation)
[ Aurora PostgreSQL 16 (Optimistic Lock) ] [ Business Rule Exception ]
├── 1. Updates `LoanDisbursement` Row └── Throws `CreditLimitExceededException`
└── 2. Writes `TrancheDisbursedEvent` Rolls back local transaction in < 2ms
to Transactional Outbox Table
Criteria and weights
| Criterion | Why it matters here | Weight | Source of the weight |
|---|---|---|---|
| Zero Multi-Aggregate Transaction Deadlocks | Concurrent operations on borrower accounts must never block tranche disbursements (DB-4910). | 0.40 | David O'Reilly (Lead Domain Architect) |
| Transactional Invariant Integrity | Aggregate must mathematically guarantee total disbursements never exceed facility caps ($50M). | 0.30 | Elena Rostova (Head of Commercial Credit) |
| Reference-by-Identity Boundary Hygiene | Aggregates must reference external entities by ID only, avoiding massive object graph memory churn. | 0.15 | Core Banking Engineering Standard |
| Concurrency Performance (Optimistic Locking) | Operations must execute in under 15 ms without database pessimistic row lock serialization. | 0.15 | Banking Transaction SLA |
Comparison
| Aggregate Design Candidate | Boundary Scope | External Entity Referencing | Transaction Concurrency | Evaluation |
|---|---|---|---|---|
| Option A: Monolithic Mega-Aggregate | Borrower + Loan + Collateral | Direct in-memory object graphs | Pessimistic DB row locks (FOR UPDATE) | Rejected: Caused DB-4910 45-minute deadlock disaster. |
| Option B: Anemic Data Entities | Separate flat database tables | Database Foreign Keys | Exposed public setters (Zero encapsulation) | Rejected: Business logic scatters into services; invariants leak. |
| Option C: Isolated DDD Aggregate Root (Chosen) | LoanDisbursement + Tranches | Reference-by-Identity (BorrowerId) | Optimistic concurrency (version column) | Selected: Atomic invariants, zero deadlocks, sub-15ms execution. |
Result
Option C is selected. LoanDisbursement encapsulates tranche lifecycle logic; reference-by-identity eliminates cross-aggregate deadlocks; optimistic versioning guarantees race-free commits.
Required Mechanisms
1. Aggregate Root Boundary & Encapsulation [MC-AR-01]
- Aggregate Root:
LoanDisbursement(Root entity owning lifecycle). - Internal Entities:
DisbursementTranche:- Tranches have local entity IDs scoped within the aggregate root.
- External callers cannot query or mutate
DisbursementTranchedirectly; all additions or state transitions must invoke methods onLoanDisbursement:loanDisbursement.requestTrancheDisbursement(trancheId, amount, margin);
2. Value Object Classification & Immutability [MC-VO-01]
Money: Contains BigDecimal amount and Currency currency. Enforces Banker's Rounding (half-even); prohibits arithmetic across mismatched currencies.
InterestRateMargin: Enforces business invariant: spread must be >=
2.50% (250 basis points). Instantiation with $< 2.50%$ throws IllegalArgumentException.
DisbursementSchedule: Encapsulates tranche due dates and maturity windows as an immutable value object collection.
3. Transactional Invariant Enforcement [MC-IE-01]
- Invariant 1 (
INV-FACILITY-CAP):
$$\sum_{i=1}^{N} \text{Tranche}_i.\text{amount} \le \text{FacilityCeiling} \quad ($50,000,000.00)$$
If a new tranche causes the aggregate total to exceed $50M, the operation throwsFacilityCeilingExceededExceptionand aborts.
Invariant 2 (INV-TRANCHE-SEQ): Tranche $N$ cannot be disbursed unless Tranche $N-1$ has achieved status SETTLED or CANCELLED.
4. Reference-by-Identity & Domain Event Outbox [MC-EV-01]
- Identity References:
BorrowerId: String identifier (borr_corp_8812).CollateralId: String identifier (collat_pledge_9941).
- Domain Event Contract:
- Upon successful tranche disbursement, the root appends
TrancheDisbursedEventto its internal domain events collection:{ "event_id": "evt_01J8N6B5H2QZ3R8V8", "aggregate_id": "disb_facility_881204", "event_type": "loan.tranche.disbursed", "occurred_at": "2026-09-15T14:22:00.123Z", "borrower_id": "borr_corp_8812", "tranche_id": "trn_04", "amount_cents": 250000000, "currency": "USD" } - Saved atomically to the local
outboxtable in the same database transaction.
- Upon successful tranche disbursement, the root appends
Invariants and Contracts
Single Aggregate Transaction Boundary [INV-AGG-01]
A single database transaction must mutate exactly one aggregate instance.
Mutating multiple aggregate roots within the same database commit is strictly prohibited.
Reference-by-Identity Invariant [INV-AGG-02]
Aggregates must not hold direct memory references or object pointers to external aggregate roots.
External entities must be referenced exclusively via immutable identity value objects.
Optimistic Concurrency Control Floor [INV-AGG-03]
The aggregate persistence entity must maintain a numerical version column (`@Version`).
Concurrent race conditions must trigger `OptimisticLockingFailureException` and roll back cleanly.
Explicit Unknowns
- Eventual consistency latency when downstream accounting ledgers consume
TrancheDisbursedEventduring peak Kafka broker rebalances (G-1). - Maximum cardinality of historical tranche entities per loan facility before aggregate rehydration latency exceeds 10 ms (G-2).
Traceability
| Claim | Classification | Source | Freshness |
|---|---|---|---|
| Commercial credit facilities up to $50M | provided | Credit domain scope intake | Current |
| Incident DB-4910 45-minute deadlock freeze | provided | Historical post-mortem | Historical |
| Interest rate margin floor >= 2.50% | provided | Credit risk policy intake | Current |
| Single-aggregate transaction boundary rule | decided | David O'Reilly (Lead Domain Architect) | 2026-09-15 |
Reference-by-identity standard (BorrowerId) | decided | Elena Rostova (Commercial Credit) | 2026-09-15 |
| Optimistic concurrency versioning | decided | Architectural invariant INV-AGG-03 | 2026-09-15 |
Verification
No validator was supplied, so no command was run.
Reviewer self-check against aggregate design standards:
- Boundary Cleanliness: PASS.
LoanDisbursementis the sole root; internal tranches are strictly encapsulated. - Reference Hygiene: PASS. External borrower and collateral entities referenced exclusively by identity strings.
- Invariant Rigor: PASS. Mathematical facility cap ($50M) and tranche sequencing enforced within the root.
- Markdown Hygiene: PASS. Native Markdown syntax strictly adheres to
rule_markdown.md.
Open Decisions
DEC-AGG-01: David O'Reilly to determine whether completed loans older than 3 years should be pruned from the active aggregate table and archived to cold Parquet lakehouses (Owner: David O'Reilly).
Next steps
- Core Engineering implements the
LoanDisbursementaggregate root andMoneyValue Object in Java 21. - Platform team sets up the transactional outbox table schema and Debezium Kafka publisher.
- Conduct concurrency stress drill simulating 35 parallel tranche disbursement requests on the same facility to verify race-free optimistic locking.
Connects securely to your tools. The creator never sees your data.
What you get
About this skill
What it does
This skill groups supplied domain state and invariants into the smallest consistency boundaries needed by a declared domain decision. It identifies aggregate roots, internal membership, lifecycle, atomic state transitions, concurrency assumptions, external references and unresolved evidence.
Use it when
Use when authoritative invariants and state transitions exist, but it is unclear which state must change atomically, which entity controls mutation, what can remain eventually consistent, or where references cross boundaries.
For example: “In our warehouse management app, two pickers can claim the last item from a bin at the exact same time, causing negative inventory counts and broken fulfillment orders.”
What you get
- DDD Aggregate Model
- Aggregate Boundary Spec
- Invariant Rules Documentation
Written as Markdown to <your output folder>/architecture/tasks/<run-id>/aggregate-design/.
What it will not do
Do not use merely to discover entities/value objects/events, design bounded contexts, scaffold DDD code, define repository/ORM/database schemas, choose event sourcing/CQRS, design services/APIs/workflows/sagas, implement tests, or refactor code.
How it works
- Check consistency boundary requirement.
- Identify domain invariants.
- Select the aggregate root.
- Define internal boundary membership.
- Establish cross-boundary reference rules.
- Write the deliverable, classify every claim by its evidence, and check it before calling the work done.
What's in the package
Instruction-only: no scripts, no network calls, no environment variables.
- LICENSE.txt
- SKILL.md
- agents/openai.yaml
- assets/output-template-task.md
- references/domain-rules.md
- references/operating-rules.md
- references/output-contract.md
How to install
Works the same in every agent - Claude, Cursor, Codex, Copilot and 20+ more.
- 1
Download the ZIP
Free skills download straight away. Paid skills unlock right after purchase.
- 2
Unzip into your skills folder
Every agent reads skills from one folder on your machine. Drop the unzipped folder in there.
- 3
Ask your agent to use it
Restart the agent if it was already running. It picks the skill up automatically - no config needed.
Skills folder by agent
Click the path to copy it. Create the folder if it does not exist yet.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill passes our trust checks below.
Security scanned
Passed our 8-point scan before listing
Fresh listing
Recently published to Agensi
30-day refund
Not a fit? Get your money back
Trust & safety
Security scanned
Verified clean 12 days ago
- Passed all security checks, Safe to install