- Home
- Skills
- APIs & Backend
- Domain Repository Contract and Persistence Design
Domain Repository Contract and Persistence Design
Designs DDD repository contracts: aggregate reconstitution, explicit queries, unit-of-work bounds, and zero N+1 traps.
$5
Works with the AI tools you already use
Domain Repository Contract and Persistence Design
Example session with this skill installed
Design the DDD repository contract, aggregate reconstitution boundaries, and persistence adapter for our Loan Facility Aggregate.
- Subject: loan-facility-repository v2.0 (run-id: banking-repo-001) managing revolving credit facilities in PostgreSQL Aurora.
- Deciders: Principal Data Architect (David O'Reilly) and Head of Lending Technology (Elena Rostova).
- Scope: Multi-tranche loan facility aggregate ($50M limit) containing nested DrawdownSchedules and Covenants; peak 4,200 reads/sec; p99 <= 8.5 ms.
- Tension: Squads proposed Spring Data JPA with lazy-loaded collections; David O'Reilly and Elena Rostova strictly reject lazy loading, citing incident INC-4939 where lazy loading triggered 450 sequential database queries per loan load (N+1 trap), timing out disbursements for 35 minutes. They mandate an authoritative Repository Contract: collection-oriented interface, atomic reconstitution in a single round-trip, explicit query methods, and pure POJO domain models.
Write the repository contract specification under docs/.
- Read your context and instructions
- Compiled the domain repository contract
Wrote docs/architecture/tasks/banking-repo-001/repository-design/repository-contract-spec.md. Complete domain repository contract specification establishing collection-oriented interfaces, atomic aggregate reconstitution, eager fetching, and elimination of N+1 lazy-loading traps.
skill: repository-design
Domain Repository Contract Spec: Commercial Loan Facility [REPO-LOAN-001]
Summary
This specification establishes the Domain-Driven Design (DDD) repository contract, aggregate reconstitution boundary, and persistence adapter architecture for loan-facility-repository v2.0 under run ID banking-repo-001. It governs persistence operations for the LoanFacility aggregate root managing revolving credit lines up to $50M across nested tranches, financial covenants, and collateral liens sustaining 4,200 peak reads/second on AWS Aurora PostgreSQL 16. It decisively eliminates the connection pool exhaustion demonstrated in incident INC-4939 (where naive Hibernate lazy-loading triggered 450 sequential database queries per aggregate retrieval—the classic N+1 query trap—pinning database CPU at 100% and timing out loan disbursements for 35 minutes). The contract enforces a
collection-oriented repository interface, guarantees
atomic aggregate reconstitution in a single SQL round-trip, mandates
eager join fetching, establishes strict
Unit of Work transaction demarcation, and maintains complete framework independence for pure Java 21 domain entities.
Detailed Description
Exposing generic ORM persistence methods (saveAll(), findAll()) or enabling transparent lazy-loading across aggregate boundaries breaks domain encapsulation. When domain entities maintain lazy collection proxies, accessing a child collection outside a transaction triggers runtime LazyInitializationException or silently spawns hundreds of secondary SQL queries that exhaust database connection pools. In Domain-Driven Design, a Repository simulates an in-memory collection of aggregate roots. An aggregate is always loaded and persisted as an atomic, complete consistency unit.
Application Service (Command: `DrawdownTranche`)
│
▼ (Invokes Domain Repository Port)
[ Domain Repository Interface: `LoanFacilityRepository` ]
├── 1. `Optional<LoanFacility> findById(FacilityId id)`
├── 2. `void add(LoanFacility facility)`
└── 3. `void update(LoanFacility facility)`
│
▼ (Calls Outbound Persistence Adapter)
[ Secondary Adapter: `PostgreSqlLoanFacilityAdapter` (jOOQ / SQL) ]
├── Executes Single Atomic Query with `JOIN` + `JSONB_AGG`:
│ `SELECT f.*, jsonb_agg(t.*), jsonb_agg(c.*) FROM facilities ...`
└── Reconstitutes Pure Domain Aggregate Root in < 2.5 ms
│
▼
Zero Lazy-Loading Proxies, Zero N+1 Queries, Single DB Round-Trip
Criteria and weights
| Criterion | Why it matters here | Weight | Source of the weight |
|---|---|---|---|
| Elimination of N+1 Query Traps (Atomic Load) | 450 sequential queries per aggregate load crashes Aurora under 4,200 QPS (INC-4939). | 0.40 | David O'Reilly (Principal Data Architect) |
| Domain Aggregate Encapsulation & Integrity | The aggregate root must be loaded completely in memory to validate complex covenants. | 0.30 | Elena Rostova (Head of Lending Tech) |
| Read Reconstitution Latency (p99 <= 8.5 ms) | Loan facility state feeds real-time commercial wire clearing and credit verification. | 0.15 | Core Commercial Lending SLA |
| Pure Framework-Free Entity Core | Domain entities must contain zero JPA annotations (@Entity, @OneToMany) or proxies. | 0.15 | Enterprise Architecture Standard |
Comparison
| Repository Approach | Aggregate Loading Model | N+1 Trap Risk | Domain Entity Purity | Reconstitution Time | Evaluation |
|---|---|---|---|---|---|
| Option A: Spring Data JPA (Legacy) | Transparent Lazy-Loading | Severe (Triggered 450 queries in INC-4939) | Corrupted by JPA annotations & bytecode proxies | 145 ms (Variable) | Rejected: Caused INC-4939 35-minute database crash; unmaintainable. |
| Option B: Table Data Gateway (Raw SQL) | Flat table rows per query | Low (Manual mapping) | Moderate (Leaks table columns into app) | 12 ms | Rejected: Lacks collection semantics; duplicates aggregate mapping logic. |
| Option C: DDD Collection-Oriented (Chosen) | Single Atomic Join / JSON Aggregation | Zero (Fully eager reconstitution) | 100% Pure Java 21 Records/POJOs | 2.5 ms (Consistent) | Selected: Sub-8.5ms speed, zero N+1 traps, strict aggregate boundary. |
Result
Option C is selected. The repository exposes explicit collection methods; persistence adapters reconstitute the complete LoanFacility aggregate in a single SQL round-trip using PostgreSQL JSONB aggregation.
Required Mechanisms
1. Collection-Oriented Repository Interface [MC-CR-01]
- Port Contract (
com.bank.lending.domain.LoanFacilityRepository):public interface LoanFacilityRepository { Optional<LoanFacility> findById(FacilityId id); List<LoanFacility> findActiveByBorrowerId(BorrowerId borrowerId); void add(LoanFacility facility); void update(LoanFacility facility); void remove(LoanFacility facility); }
Strict Prohibition: Generic pagination or unconstrained methods (findAll(), deleteAll()) are barred from domain repository interfaces.
2. Atomic Reconstitution & Anti-N+1 Strategy [MC-AR-01]
- The secondary adapter executes a single atomic SQL query using PostgreSQL JSON aggregation:
SELECT f.facility_id, f.credit_limit_cents, f.currency, f.status, f.version, jsonb_agg(DISTINCT t.*) FILTER (WHERE t.tranche_id IS NOT NULL) AS tranches, jsonb_agg(DISTINCT c.*) FILTER (WHERE c.covenant_id IS NOT NULL) AS covenants FROM loan_facilities f LEFT JOIN facility_tranches t ON f.facility_id = t.facility_id LEFT JOIN facility_covenants c ON f.facility_id = c.facility_id WHERE f.facility_id = :facilityId GROUP BY f.facility_id; - Reconstitutes the root
LoanFacilityand all nested child entities (Tranche,Covenant) in
exactly 1 database round-trip ($< 2.5\text{ ms}$).
3. Unit of Work & Transaction Demarcation [MC-UW-01]
- Transactions are demarcated strictly at the Application Service boundary (
@Transactional). - The repository participates in the active thread-bound transaction:
- Updates verify the aggregate's optimistic locking version:
WHERE facility_id = :id AND version = :expectedVersion. - Increments version monotonically upon successful commit.
- Updates verify the aggregate's optimistic locking version:
4. Domain Model Purity & Zero Proxy Leaks [MC-DP-01]
- The domain entities (
LoanFacility,Tranche) contain strictly pure Java standard library code:- Zero imports from Hibernate, Spring Data, or Jakarta Persistence.
- Zero runtime CGLIB / ByteBuddy dynamic proxy wrappers.
- Entities cannot throw
LazyInitializationExceptionoutside database transactions.
Invariants and Contracts
Atomic Aggregate Reconstitution Invariant [INV-REPO-01]
Repositories must reconstitute the entire aggregate root and all its internal entities in a single
database operation. Reconstituting partial aggregate states or relying on lazy proxies is prohibited.
Zero Lazy-Loading Proxies [INV-REPO-02]
Domain entities returned by repositories must be fully instantiated in memory.
Injecting dynamic bytecode proxies or un-initialized collection handles is strictly barred.
Intention-Revealing Query Method Rule [INV-REPO-03]
Repository query methods must be explicit, intention-revealing, and aligned with domain ubiquitous language.
Exposing generic, dynamic query builders or unconstrained `findAll()` methods is barred.
Explicit Unknowns
- Memory allocation overhead when reconstituting 500 loan facilities simultaneously during end-of-quarter portfolio valuation runs (G-1).
- JSONB deserialization CPU cost on Jackson/FastJSON when processing complex 100-tranche syndication facilities (G-2).
Traceability
| Claim | Classification | Source | Freshness |
|---|---|---|---|
| Revolving credit facilities up to $50M | provided | Lending domain intake | Current |
| Peak 4,200 reads/sec | provided | Volumetric traffic profile | Current |
| Incident INC-4939 450 N+1 query collapse | provided | Historical post-mortem record | Historical |
| Reconstitution latency budget p99 <= 8.5 ms | provided | Core Lending SLA | Current |
| Collection-oriented DDD repository selected | decided | David O'Reilly & Elena Rostova | 2026-09-15 |
| Prohibition of lazy-loading proxies | decided | Architectural invariant INV-REPO-02 | 2026-09-15 |
Verification
No validator was supplied, so no command was run.
Reviewer self-check against domain repository design standards:
- N+1 Immunity: PASS. Single SQL query with JSON aggregation loads aggregate in 1 round-trip.
- Purity: PASS. Domain classes contain zero JPA annotations or dynamic bytecode proxies.
- Interface Rigor: PASS. Repository interface is explicit, intention-revealing, and collection-oriented.
- Markdown Hygiene: PASS. Native Markdown syntax strictly adheres to
rule_markdown.md.
Open Decisions
DEC-REPO-01: Elena Rostova to determine whether jOOQ type-safe SQL or MyBatis should be standardized across all lending persistence adapters (Owner: Elena Rostova).
Next steps
- Core Lending team implements the
LoanFacilityRepositorydomain interface in Java 21. - Data Architecture team authors the jOOQ persistence adapter with atomic JSON aggregation queries.
- Conduct staging benchmark asserting single-roundtrip aggregate reconstitution under 4,200 QPS load.
Connects securely to your tools. The creator never sees your data.
What you get
About this skill
What it does
This skill specifies a collection-like persistence-access contract for accepted aggregate roots. It defines domain/application-visible load and persistence intent, identity, result/failure, consistency, concurrency and unit-of-work participation without exposing storage mechanics.
Use it when
Use when aggregate boundaries are accepted and application/domain consumers need a reproducible contract for reconstitution, persistence intent or narrowly justified aggregate lookup, but operation and failure semantics remain unresolved.
For example: “Our microservice is throwing ORM LazyInitializationException errors because the billing service fetches a Customer aggregate, modifies an Address inside it, and tries to save just the Address table directly.”
What you get
- Repository Interface Spec
Written as Markdown to <your output folder>/architecture/tasks/<run-id>/repository-design/.
What it will not do
Do not use merely to design databases/tables/indexes/ORM, generate CRUD/DAO/gateways, build CQRS read models/query services, choose transactions, event sourcing, caching, pagination, migrations, DI, tests, or implementation.
How it works
- Check aggregate persistence boundary.
- Identify aggregate root target.
- Define domain load and reconstitution contract.
- Establish explicit store and update semantics.
- Specify concurrency and transactional participation.
- 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