Arquitectura en capas (Layered)
Las capas típicas de una aplicación web
Sección titulada «Las capas típicas de una aplicación web»┌─────────────────────────────┐│ Presentación (HTTP / API) │ routes, controllers├─────────────────────────────┤│ Aplicación / Negocio │ services, casos de uso├─────────────────────────────┤│ Acceso a datos │ repositories├─────────────────────────────┤│ Base de datos │└─────────────────────────────┘La regla central: cada capa solo puede hablar con la capa inmediatamente debajo. Presentación no le habla directo a la base de datos. Nunca se salta una capa, aunque “sea más rápido” en el momento.
Layered vs. MVC
Sección titulada «Layered vs. MVC»Son cosas distintas que se confunden seguido. Layered architecture es un principio general de organización en niveles horizontales — aplica a cualquier tipo de app. MVC es una forma específica de organizar la capa de presentación + control (y a veces también el model). En la práctica, un backend con estructura MVC (ver MVC overview) es un caso particular de arquitectura en capas: controller = presentación, service = aplicación/negocio, repository = acceso a datos.
La regla que más se rompe
Sección titulada «La regla que más se rompe»// controller saltándose la capa de servicioapp.get("/users/:id", async (req, res) => { const user = await prisma.user.findUnique({ where: { id: req.params.id } }) res.json(user)})Funciona hoy. Duele después:
- Cambiar de ORM o de base de datos significa buscar y tocar cada controller que hace queries directo, en vez de cambiar una sola capa de acceso a datos.
- Testear el controller sin una base de datos real se vuelve imposible — no hay forma de inyectar un repositorio falso porque no hay repositorio, hay una llamada a Prisma incrustada.
- Agregar una regla de negocio (validar algo antes de devolver el user, por ejemplo) no tiene un lugar claro donde vivir — o se mete en el controller o se duplica en cada endpoint que necesite lo mismo.
La versión correcta pasa por un service intermedio:
app.get("/users/:id", async (req, res) => { const user = await userService.getById(req.params.id) res.json(user)})El controller ya no sabe que existe Prisma. Ese conocimiento vive en una sola capa, reemplazable sin tocar el resto.