This skill should be used when the user asks to "add a repository port", "create a Prisma adapter", "add an outbound port", "add a database repository", or "scaffold a repository interface".
Declare an outbound repository port (interface) and implement a Prisma adapter with domain-to-DB mapping.
moduleName (required): existing module namerepoName (required): PascalCase (e.g., ArticleRepository)entityName (required): domain entity name (e.g., Article)operations (required): array of method signatures (e.g., ['create', 'findById', 'list'])Creates inside apps/backend/src/modules/<moduleName>/:
ports/outbound/<repo-name>.ts — TypeScript interface with declared operationsadapters/persistence/prisma/<entity-name>.mapper.ts — domain to Prisma model mapperadapters/persistence/prisma/<repo-name>.prisma.ts — Prisma implementation of the interface@prisma/client installed and generated (pnpm prisma generate run)entityName exists in apps/backend/prisma/schema.prismaports/outbound/<repo-name>.ts:export interface <RepoName> {
create(data: Create<EntityName>Data): Promise<<EntityName>>;
findById(id: string): Promise<<EntityName> | null>;
// TODO: add operations listed in inputs
}
export type Create<EntityName>Data = {
// TODO: fields needed to create
};
export type <EntityName> = {
id: string;
// TODO: domain entity fields
};
adapters/persistence/prisma/<entity-name>.mapper.ts:import type { <PrismaModelName> } from '@prisma/client';
import type { <EntityName> } from '../../../ports/outbound/<repo-name>';
export function toDomain(record: <PrismaModelName>): <EntityName> {
return {
id: record.id,
// TODO: map Prisma fields to domain fields
};
}
export function toPrismaCreate(data: Create<EntityName>Data): Omit<<PrismaModelName>, 'id' | 'createdAt' | 'updatedAt'> {
return {
// TODO: map domain create data to Prisma input fields
} as any;
}
Note: <PrismaModelName> is typically the same as entityName in PascalCase — verify the exact name in apps/backend/prisma/schema.prisma.
adapters/persistence/prisma/<repo-name>.prisma.ts:import type { PrismaClient } from '@prisma/client';
import type { <RepoName>, Create<EntityName>Data, <EntityName> } from '../../../ports/outbound/<repo-name>';
import { toDomain, toPrismaCreate } from './<entity-name>.mapper';
export class Prisma<RepoName> implements <RepoName> {
constructor(private readonly prisma: PrismaClient) {}
async create(data: Create<EntityName>Data): Promise<<EntityName>> {
const record = await this.prisma.<modelName>.create({ data: toPrismaCreate(data) });
return toDomain(record);
}
async findById(id: string): Promise<<EntityName> | null> {
const record = await this.prisma.<modelName>.findUnique({ where: { id } });
return record ? toDomain(record) : null;
}
// TODO: implement remaining operations
}
index.tspnpm -C apps/backend typecheckE_PRISMA_CLIENT_MISSING: @prisma/client not installed or schema not generated → run pnpm -C apps/backend prisma generateE_PORT_EXISTS: Port interface file already exists → ask user to extend or overwriteE_TYPECHECK_UNAVAILABLE: typecheck script missing → run pnpm -C apps/backend tsc --noEmit directlySee docs/project-overview.md → "BE-04 — Add repository port + Prisma adapter" for the port/adapter/mapper pattern.