|
| 1 | +# Copilot Instructions – @backendworks/post-db |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Shared npm package published to **GitHub Packages** (`@backendworks/post-db`). Owns the **Post Prisma schema**, all database migrations, and exposes an **ORM-agnostic repository interface** so `post-service` and `post-worker` never import `PrismaClient` directly. |
| 6 | + |
| 7 | +Both `post-service` and `post-worker` must always pin to the **same version range** of this package. |
| 8 | + |
| 9 | +## Versioning Rules |
| 10 | + |
| 11 | +| Change type | Version bump | |
| 12 | +| ------------------------------------ | ------------ | |
| 13 | +| Schema change (new field, new model) | **minor** | |
| 14 | +| Breaking interface change | **major** | |
| 15 | +| Internal Prisma query fix | **patch** | |
| 16 | + |
| 17 | +## Developer Workflows |
| 18 | + |
| 19 | +Run all commands from the `packages/post-db/` directory: |
| 20 | + |
| 21 | +```bash |
| 22 | +npm run prisma:migrate # dotenv -e .env.docker -- prisma migrate dev |
| 23 | +npm run prisma:generate # regenerates Prisma client from schema |
| 24 | +npm run prisma:studio # opens Prisma Studio against the post DB |
| 25 | +npm run build # tsc compile → dist/ (run before publishing) |
| 26 | +npm publish # publishes to GitHub Packages (requires GITHUB_TOKEN) |
| 27 | +``` |
| 28 | + |
| 29 | +## ORM-Agnostic Design |
| 30 | + |
| 31 | +The package exposes interfaces, not Prisma types. Apps interact only with the public API: |
| 32 | + |
| 33 | +```typescript |
| 34 | +import { |
| 35 | + createPostDbManager, |
| 36 | + IPostDbManager, |
| 37 | + IPostRepository, |
| 38 | +} from "@backendworks/post-db"; |
| 39 | + |
| 40 | +// Wire up once at module init (NestJS provider factory) |
| 41 | +const dbManager: IPostDbManager = createPostDbManager(process.env.DATABASE_URL); |
| 42 | + |
| 43 | +// Inject IPostRepository in services — never PrismaClient |
| 44 | +const post = await dbManager.postRepository.findById(postId); |
| 45 | +``` |
| 46 | + |
| 47 | +### Swapping the ORM |
| 48 | + |
| 49 | +To replace Prisma with TypeORM or Drizzle: |
| 50 | + |
| 51 | +1. Replace `src/client/prisma.client.ts` with the new client factory |
| 52 | +2. Replace `src/repositories/post.repository.ts` with a new implementation of `IPostRepository` |
| 53 | +3. **Do not touch** `src/interfaces/` or `src/index.ts` — the contract stays the same |
| 54 | +4. Run `npm run build` and bump the package version |
| 55 | + |
| 56 | +## Schema Ownership |
| 57 | + |
| 58 | +`prisma/schema.prisma` is the **single source of truth** for the post database. No app or worker has its own `schema.prisma`. |
| 59 | + |
| 60 | +Current schema covers: |
| 61 | + |
| 62 | +- `Post` — id (UUID), title, content, images (String[]), createdBy (UUID — references User in auth DB), deletedAt?, isDeleted (Boolean), createdAt, updatedAt |
| 63 | + |
| 64 | +Note: `createdBy` is a plain UUID — there is **no foreign key** to the auth DB. Author enrichment happens at the service layer via `GrpcAuthService.getUserById()`. |
| 65 | + |
| 66 | +### Running a migration |
| 67 | + |
| 68 | +```bash |
| 69 | +# From packages/post-db/ |
| 70 | +npm run prisma:migrate # prompts for migration name, applies to dev DB |
| 71 | +``` |
| 72 | + |
| 73 | +Always bump the package **minor version** after a schema change and update both `post-service` and `post-worker` to the new version. |
| 74 | + |
| 75 | +## Public API (`src/index.ts`) |
| 76 | + |
| 77 | +```typescript |
| 78 | +export { IPostDbManager } from "./interfaces/db-manager.interface"; |
| 79 | +export { IPostRepository } from "./interfaces/post.repository.interface"; |
| 80 | +export { createPostDbManager } from "./client/prisma.client"; |
| 81 | +``` |
| 82 | + |
| 83 | +Never export `PrismaClient`, raw Prisma types, or implementation details. |
| 84 | + |
| 85 | +## Folder Structure |
| 86 | + |
| 87 | +``` |
| 88 | +packages/post-db/ |
| 89 | + prisma/ |
| 90 | + schema.prisma # Post — source of truth for post DB |
| 91 | + migrations/ # All post DB migrations — never edit manually |
| 92 | + src/ |
| 93 | + client/ |
| 94 | + prisma.client.ts # PrismaClient singleton + createPostDbManager() factory |
| 95 | + interfaces/ |
| 96 | + post.repository.interface.ts # IPostRepository — ORM-agnostic CRUD contract |
| 97 | + db-manager.interface.ts # IPostDbManager { postRepository: IPostRepository } |
| 98 | + repositories/ |
| 99 | + post.repository.ts # Concrete Prisma implementation of IPostRepository |
| 100 | + operations/ |
| 101 | + base.operations.ts # Generic helpers: findById, findOne, findMany, |
| 102 | + # create, update, softDelete (sets deletedAt + isDeleted) |
| 103 | + index.ts # Public API — ONLY thing consumers should import from |
| 104 | + package.json # name: "@backendworks/post-db", "main": "dist/index.js" |
| 105 | + tsconfig.json # Compiles src/ → dist/ |
| 106 | +``` |
| 107 | + |
| 108 | +## IPostRepository Interface Contract |
| 109 | + |
| 110 | +```typescript |
| 111 | +interface IPostRepository { |
| 112 | + findById(id: string): Promise<Post | null>; |
| 113 | + findOne(filter: Partial<Post>): Promise<Post | null>; |
| 114 | + findMany(options: QueryOptions): Promise<PaginatedResult<Post>>; |
| 115 | + create(data: CreatePostInput): Promise<Post>; |
| 116 | + update(id: string, data: Partial<Post>): Promise<Post>; |
| 117 | + softDelete(id: string): Promise<Post>; // sets deletedAt + isDeleted: true |
| 118 | + softDeleteMany(ids: string[]): Promise<number>; // bulk soft-delete, returns count |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +All methods treat `deletedAt: null` as the baseline — soft-deleted posts are never returned unless explicitly queried. |
| 123 | + |
| 124 | +## Testing Conventions |
| 125 | + |
| 126 | +- Unit tests in `test/unit/` with 100% coverage on `*.repository.ts` and `*.operations.ts` |
| 127 | +- Mock `PrismaClient` at the method level: `prisma.post.findUnique = jest.fn()` |
| 128 | +- Integration tests (if any) run against a real Docker Postgres — never the production DB |
0 commit comments