Best practices for organizing Prisma schema in medium-sized projects? #29901
Replies: 3 comments 4 replies
|
For 30–50 models, I would use the multi-file schema, but split by domain rather than creating one file per model. A practical layout is: With current Prisma, point import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
},
})Prisma loads the A few rules have kept this manageable in larger projects:
If the project is still small and the domain boundaries are unclear, one file is fine. At 30–50 models, though, a domain-based split usually pays for itself without changing how Prisma Client or migrations work. |
|
paulodearaujo's folder-per-domain layout is the right shape for the schema side. One thing worth adding for the "how do you organize... Prisma Client" part of your question, since that's less settled than the schema split. The instinct to avoid a client-per-domain is correct (you'd end up with N connection pools and no single source of truth for // src/db/extensions/billing.ts
import { Prisma } from '../generated/client';
export const billingExtension = Prisma.defineExtension({
name: 'billing',
model: {
invoice: {
async markPaid(id: string) {
return prisma.invoice.update({ where: { id }, data: { status: 'PAID', paidAt: new Date() } });
},
},
},
});// src/db.ts
import { PrismaClient } from './generated/client';
import { billingExtension } from './extensions/billing';
import { catalogExtension } from './extensions/catalog';
const prisma = new PrismaClient().$extends(billingExtension).$extends(catalogExtension);
export default prisma;This gives you the same domain boundaries you already have in the schema files, but on the client side. One more thing worth locking down early at your scale: seed script organization. With |
|
Good timing on this question — Prisma's multi-file schema support changed things a lot for projects at your scale. On the schema organization question For 30–50 models, the multi-file schema is the right move, but the key is splitting by domain rather than by model. One file per model will leave you with 50 tiny files that are hard to navigate. One big file has obvious problems. Domain-based split hits the sweet spot: Prisma loads all On PrismaClient organization This part trips up a lot of projects at your scale. The core rule: one PrismaClient instance for the whole app. Multiple instances = multiple connection pools, which causes issues under load. Export a singleton: // src/db/client.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;For query organization at 30–50 models, I'd recommend a pragmatic split rather than going full repository pattern everywhere:
For example: // src/billing/billing.queries.ts
export const invoiceFilters = {
forCustomer: (id: string): Prisma.InvoiceWhereInput => ({ customerId: id }),
unpaid: (): Prisma.InvoiceWhereInput => ({ status: { in: ['PENDING', 'OVERDUE'] } }),
};
// src/billing/billing.service.ts
const invoices = await prisma.invoice.findMany({
where: {
...invoiceFilters.forCustomer(customerId),
...invoiceFilters.unpaid(),
},
});This avoids the overhead of full repository classes while still keeping reusable query logic in one place. On migrations Keep one Quick summary for your scale
At 30–50 models you have enough to feel the pain of bad organization but not so much that you need a heavy repository abstraction everywhere — this middle path handles it well. |
Uh oh!
There was an error while loading. Please reload this page.
I'm building a full-stack application using Prisma with PostgreSQL and expect around 30–50 models in the future.
What are the recommended practices for organizing the Prisma schema?
I'd appreciate any advice or examples from production projects.
All reactions