Ir al contenido
angel.library

Repository pattern

El resto de la aplicación no debería saber ni necesitar saber si los datos vienen de Prisma, de SQL directo o de una API externa. Solo debería conocer una interfaz en términos del dominio:

repositories/user-repository.ts (interfaz)
export interface UserRepository {
findActiveUsers(): Promise<User[]>
findById(id: string): Promise<User | null>
save(user: User): Promise<void>
}

findActiveUsers(), no SELECT * FROM users WHERE active = true. La interfaz habla el lenguaje del negocio, no el lenguaje de la base de datos.

repositories/prisma-user-repository.ts
import type { PrismaClient } from "@prisma/client"
import type { UserRepository } from "./user-repository"
export class PrismaUserRepository implements UserRepository {
constructor(private readonly prisma: PrismaClient) {}
findActiveUsers() {
return this.prisma.user.findMany({ where: { active: true } })
}
findById(id: string) {
return this.prisma.user.findUnique({ where: { id } })
}
async save(user: User) {
await this.prisma.user.upsert({
where: { id: user.id },
update: user,
create: user
})
}
}

El service que usa este repositorio solo conoce UserRepository, no PrismaUserRepository:

services/user-service.ts
export class UserService {
constructor(private readonly repo: UserRepository) {}
listActive() {
return this.repo.findActiveUsers()
}
}
  • Cambiar de ORM (Prisma → Drizzle, por ejemplo) significa escribir una nueva clase que implemente UserRepository — cero cambios en services.
  • Testear sin base de datos real — un InMemoryUserRepository que implemente la misma interfaz sirve para tests rápidos y deterministas.

Este patrón es, en términos de arquitectura hexagonal, un puerto (UserRepository) con sus adaptadores (PrismaUserRepository, InMemoryUserRepository) — ver Arquitectura hexagonal para el concepto general de puertos y adaptadores.

En un proyecto chico donde nunca vas a cambiar de ORM ni necesitas testear sin DB, el repository es una capa de indirección que solo agrega archivos: una interfaz, una implementación, y el mismo código que hubieras escrito llamando a Prisma directo desde el service. Si esa flexibilidad nunca se va a usar, no está agregando valor real — solo ceremonial.