Implementing Design Patterns
1. Implementing Repository Pattern
export class UserRepository {
constructor(private prisma: PrismaClient) {}
findById(id: number) { return this.prisma.user.findUnique({ where: { id } }); }
create(data: Prisma.UserCreateInput) { return this.prisma.user.create({ data }); }
}
| Benefit | Detail |
| Encapsulation | Hide query details |
| Testability | Mock the repo, not Prisma |
2. Using Unit of Work Pattern
await prisma.$transaction(async (tx) => {
await new UserRepository(tx).create(userData);
await new AuditRepository(tx).log("user.create");
});
| Aspect | Detail |
| Single tx | All-or-nothing |
| Repo injection | Pass tx client to repos |
3. Implementing Service Layer
| Responsibility | Detail |
| Use cases | Orchestrate repos + domain logic |
| Transactions | Define boundaries |
| Validation | Domain invariants |
4. Using Singleton Pattern
// lib/prisma.ts
import { PrismaClient } from "@prisma/client";
const globalAny = global as any;
export const prisma: PrismaClient = globalAny.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== "production") globalAny.prisma = prisma;
| Reason | Detail |
| Hot reload | Avoid pool exhaustion in Next.js dev |
| Single pool | One client per process |
5. Implementing Factory Pattern
| Use | Detail |
| Per-tenant client | Factory builds client by URL |
| Test data | Factory functions for fixtures |
6. Using Builder Pattern for Queries
class UserQuery {
private args: Prisma.UserFindManyArgs = {};
where(w: Prisma.UserWhereInput) { this.args.where = { ...this.args.where, ...w }; return this; }
paginate(skip: number, take: number) { Object.assign(this.args, { skip, take }); return this; }
exec(p: PrismaClient) { return p.user.findMany(this.args); }
}
| Benefit | Detail |
| Fluency | Chainable filters |
| Reuse | Compose conditions |
7. Implementing Observer Pattern
| Mechanism | Detail |
| Pulse | DB-level observer |
| Extension hooks | App-level observation |
| EventEmitter | In-process pub/sub |
8. Using Strategy Pattern
| Example | Detail |
| Pricing | Swap pricing strategies |
| Notification | Email/SMS/Push providers |
9. Implementing CQRS Pattern
Commands
- Write side
- Validate + persist
- Use transactions
Queries
- Read side
- Optimized projections
- Cacheable via Accelerate
10. Using Domain-Driven Design
| Concept | Detail |
| Aggregates | Root entity + invariants |
| Value objects | Immutable types |
| Bounded context | Separate Prisma schemas per context |