Ir al contenido
angel.library

Inyección de dependencias

antes: dependencia construida adentro
export async function getActiveUsers() {
const prisma = new PrismaClient() // construida aquí adentro
return prisma.user.findMany({ where: { active: true } })
}

Esta función no se puede testear sin una base de datos real conectada. No hay forma de “meterle” otra implementación — la dependencia está hardcodeada.

después: constructor injection
export class UserService {
constructor(private readonly repo: UserRepository) {}
getActiveUsers() {
return this.repo.findActiveUsers();
}
}
// en producción
const service = new UserService(new PrismaUserRepository(prisma));
// en un test
const service = new UserService(new InMemoryUserRepository([user1, user2]));

O más simple todavía, inyección por parámetro de función, sin siquiera una clase:

alternativa: inyección por parámetro
export function getActiveUsers(repo: UserRepository) {
return repo.findActiveUsers()
}
// test
getActiveUsers(new InMemoryUserRepository([user1, user2]))

En ambos casos, UserService (o getActiveUsers) ya no decide qué implementación de UserRepository usar — solo declara que necesita una. Quien lo llama decide cuál.

En Java o .NET, la inyección de dependencias casi siempre viene acompañada de un contenedor de DI pesado (Spring, .NET DI container) que resuelve el grafo de dependencias automáticamente. En JS/TS, para la mayoría de proyectos web, con pasar la dependencia como argumento alcanza. No hace falta ningún framework para tener los beneficios del patrón: testeable, reemplazable, desacoplado.

Existen contenedores de DI para TypeScript (tsyringe, InversifyJS) que automatizan la construcción del grafo de dependencias con decoradores. Son útiles en backends grandes con muchas capas y dependencias cruzadas, donde armar el grafo a mano se vuelve tedioso. Para empezar, o para la mayoría de proyectos, no son necesarios — constructor injection simple cubre el caso de uso real.