Arquitectura
Principios de Diseño
Section titled “Principios de Diseño”Esta API sigue una arquitectura en capas inspirada en ‘Clean Architecture’ y principios UNIX: modularidad, separación de responsabilidades, y componentes desacoplados que pueden evolucionar independientemente.
Nota Tecnica Realista: Esta API realmente implementa Layered Architecture (3-Tier) con Transaction Script Pattern, separando presentación (Routes/Controllers), lógica de negocio (Services), y acceso a datos (Models) en capas horizontales desacopladas.
Filosofía core:
- KISS: Simplicidad sobre abstracción prematura
- DRY: Normalización de códigos HTTP, mensajes de error y lógica de negocio
- Separation of Concerns: Cada capa tiene una responsabilidad única y bien definida
- Dependency Flow: Las dependencias fluyen hacia adentro (Routes → Controllers → Services → Models)
Capas y Responsabilidades
Section titled “Capas y Responsabilidades”| Capa | Responsabilidad | Maneja Estado | Depende de |
|---|---|---|---|
| Routes | Define endpoints HTTP, aplica middlewares, delega a controller | No | Controllers |
| Controllers | Valida request, coordina service, serializa response HTTP | No | Services |
| Services | Lógica de dominio, orquestación, transformaciones de datos | Opcional | Models |
| Models | Acceso directo a persistencia (queries, CRUD atómico) | Opcional | Firebase/DB |
Detalles por Capa
Section titled “Detalles por Capa”Routes: Punto de entrada HTTP. Registra endpoints, aplica middlewares (auth, CORS), y delega inmediatamente al controlador. No contiene lógica de negocio.
Controllers: Capa
de coordinación. Valida payloads y
parámetros de entrada, invoca el
servicio apropiado, captura errores
y delega al error handler, serializa
respuestas usando
sendResponse()
con códigos HTTP normalizados.
Services: Core de
la lógica de negocio. Orquesta
operaciones complejas (crear
producto con ID autoincrementable),
transforma datos entre capas,
gestiona reglas de dominio
(validación de credenciales, demo
user), retorna objetos de estado
estandarizados ({success, status, data}).
Models: Capa de persistencia pura. Ejecuta queries directas a Firebase (addDoc, getDocs, updateDoc, deleteDoc), maneja errores de conexión/query, retorna datos crudos o undefined. No contiene lógica de negocio.
Flujo de Datos
Section titled “Flujo de Datos”Request Flow:
HTTP Request → Route → Controller → Service → Model → Firebase
Response Flow:
Firebase → Model (data/throw) → Service (result object) → Controller (HTTP serialization) → Client
Error Flow:
Model (throw) → Service (propagate) → Controller (next(err)) → errorHandler → HTTP Error Response
Convenciones de Código
Section titled “Convenciones de Código”Controllers y Services: Namespace Pattern
Section titled “Controllers y Services: Namespace Pattern”Funciones puras exportadas como namespace. Sin clases, sin estado compartido.
Nomenclatura estándar CRUD:
-
create(req, res, next)- POST -
getAll(req, res, next)- GET collection -
getById(req, res, next)- GET resource -
update(req, res, next)- PATCH (partial update) -
remove(req, res, next)- DELETE (evita shadowing de keyworddelete)
Importación:
import * as productController from '#api/products/product.controller.js';
router.post('/create', auth, productController.create);
Normalización de Respuestas
Section titled “Normalización de Respuestas”
Todas las respuestas HTTP usan
sendResponse()
para garantizar formato consistente:
// Success{ success: true, code: 200, data: {...} }
// Error{ success: false, code: 400, error: "Datos inválidos o incompletos" }
Abstracciones:
-
STATUS: Símbolos para estados (STATUS.SUCCESS,STATUS.NOT_FOUND) -
HTTP_MAP: Mapeo de símbolos a códigos numéricos HTTP -
ERROR_MESSAGES: Mensajes normalizados, evita magic strings
Persistencia: Firebase Firestore
Section titled “Persistencia: Firebase Firestore”Características
Section titled “Características”- NoSQL documental: Colecciones de documentos con esquema flexible
-
Queries indexadas:
where(),orderBy(),limit()- no son SQL - Sin servidor propio: Managed service, escalado automático
- Limitaciones: Joins inexistentes, queries complejas requieren denormalización
Diferencias con MongoDB
Section titled “Diferencias con MongoDB”| Aspecto | Firestore | MongoDB |
|---|---|---|
| Formato | JSON nativo | BSON (Binary JSON) |
| Hosting | Managed (GCP) | Self-hosted / Atlas |
| Queries | Limitadas, indexadas automáticamente | Agregaciones complejas, pipelines |
| Transacciones | Soportadas (ACID en documentos relacionados) | Soportadas (ACID multi-documento) |
Estructura Actual
Section titled “Estructura Actual”users/ └─ {userId} ├─ username: string └─ password: string (⚠️ plaintext, solo demo)
products/ └─ {productId} ├─ id: number (internal autoincrement) ├─ name: string ├─ price: number └─ categories: array<string>
ID Management
Section titled “ID Management”
Problema conocido:
Race conditions en
createProduct()
al obtener
lastId de
forma no transaccional.
Solución actual: Pragmática, suficiente para carga baja/media.
Solución robusta: Usar transacciones de Firestore
Seguridad y Limitaciones
Section titled “Seguridad y Limitaciones”Implementado
Section titled “Implementado”- ✅ JWT con expiración configurable
- ✅ Middleware de autenticación en rutas protegidas
- ✅ CORS configurado para dominios específicos
- ✅ Validación de payloads en controllers
- ✅ Rate limiting vía Vercel WAF
Pendiente (Production Readiness)
Section titled “Pendiente (Production Readiness)”- ⚠️ Passwords en plaintext: Implementar bcrypt/argon2 hashing
- ⚠️ Validación limitada: Faltan límites de longitud, sanitización XSS
- ⚠️ Sin logging estructurado: console.error no escala en producción
- ⚠️ Sin tests automatizados: Cobertura manual con Postman/HTTP files
Escalabilidad y Evolución
Section titled “Escalabilidad y Evolución”Ventajas de la Arquitectura Actual
Section titled “Ventajas de la Arquitectura Actual”
Desacoplamiento
horizontal: Agregar nuevas entidades (orders,
categories) solo requiere nuevo
módulo en
api/, sin
modificar infraestructura existente.
Migración de
persistencia: Cambiar Firebase por
PostgreSQL/MongoDB solo afecta la
capa de Models. Services apenas
mutan y Controllers permanecen
intactos gracias a la abstracción de
result
objects.
Testing incremental: Cada capa puede testearse independientemente (unit tests en Services, integration tests en Controllers, E2E en Routes).
Próximos Pasos Arquitectónicos
Section titled “Próximos Pasos Arquitectónicos”En un nuevo repositorio, o como extensión escalable de este mismo:
- Transacciones en Models: Resolver race conditions con Firestore transactions
- Middleware de validación: Extraer validaciones repetitivas a middleware reutilizable con Joi/Zod
- Error types personalizados: Clases de error específicas (ValidationError, AuthError) para mejor debugging
- Repository pattern: Abstraer queries complejas en repositorios dedicados si Firestore queries crecen