← ClaudeAtlas

ts-ddd-repository-patternlisted

Design and implement the Repository pattern in a TypeScript DDD clean architecture project — define repository interfaces in the domain layer and implementations in the infrastructure layer. Trigger when the user says "create a repository", "implement persistence", "add database access", "wire up TypeORM/Prisma/Drizzle", "implement the repository interface", "add Unit of Work", "how do I persist this aggregate", or when connecting domain aggregates to any data store. Also trigger when reviewing persistence code that may be violating the dependency rule.
Methasit-Pun/ts-ddd-clean-architecture · ★ 0 · API & Backend · score 68
Install: claude install-skill Methasit-Pun/ts-ddd-clean-architecture
# Repository Pattern — TypeScript DDD Repositories are the bridge between the domain and persistence. The **interface** lives in the domain layer; the **implementation** lives in infrastructure. This is the dependency inversion principle in action — domain depends on nothing outside itself. ## Core rule > The domain defines *what* it needs. Infrastructure decides *how* to deliver it. --- ## Folder structure ``` src/[context]/ domain/ repositories/ I[AggregateName]Repository.ts ← interface (domain owns this) infrastructure/ persistence/ [AggregateName]Repository.ts ← concrete implementation [AggregateName]OrmModel.ts ← ORM/DB mapping model (infrastructure only) mappers/ [AggregateName]Mapper.ts ← maps between ORM model ↔ domain aggregate ``` --- ## Domain repository interface Keep it minimal — only the operations the domain actually needs: ```typescript // src/[context]/domain/repositories/IOrderRepository.ts export interface IOrderRepository { findById(id: OrderId): Promise<Order | null>; save(order: Order): Promise<void>; delete(id: OrderId): Promise<void>; } ``` **Interface rules:** - Parameters and return types use **domain types only** (Aggregates, Value Objects, Domain IDs) - No ORM types, no database primitives, no `Promise<any>` - Method names reflect the ubiquitous language, not SQL (`findById`, not `selectByPrimaryKey`) - `save` handles both insert and update (upsert semantics) — callers