Durante seis módulos, Escena Viva ha vivido de un fichero: datos/eventos.json. Nos ha servido bien. Nos enseñó fs.promises, nos permitió construir un dominio rico con Evento y Sesion, nos dio de comer para el servidor node:http del módulo 4 y para la API Express del módulo 6. Pero al cerrar aquel módulo dejamos una promesa pendiente: el problema del aforo y la sobreventa no se resolvía con validación ni con errores bien tipados, porque no es un problema de forma ni de estado, sino de concurrencia y persistencia. Ese problema es la puerta de entrada a este módulo.
Aquí no vamos a escribir todavía ni un esquema de Mongoose ni una sentencia CREATE TABLE. Vamos a hacer algo más importante: entender qué nos falta, qué nos da un sistema gestor de bases de datos a cambio de qué, cómo se elige entre el mundo relacional y el documental sin caer en bandos, y cómo se diseña el modelo de datos de Escena Viva. Al final tendrás el plano completo de lo que construiremos en las cinco lecciones siguientes.
Contenido
- Por qué un fichero JSON deja de servir
- Qué aporta realmente un SGBD
- Relacional frente a documental
- ACID, BASE y el teorema CAP aplicados a un aforo
- El panorama de bases de datos para Node.js
- Controlador nativo frente a ORM/ODM
- Diseñar el modelo de datos de Escena Viva
- El modelo definitivo en un diagrama
- Aislar la persistencia: el patrón repositorio
- Instalar MongoDB y comprobar la conexión
Por qué un fichero JSON deja de servir
Seamos honestos y concretos. src/catalogo-datos.js no es un mal módulo: lee datos/eventos.json con fs.promises, memoriza el resultado y expone obtenerCatalogo() y obtenerEventoPorId(). El problema no es su código, es su modelo de almacenamiento. Estos son los fallos reales, no teóricos, que Escena Viva ya tiene hoy:
- Escrituras concurrentes que se pisan. Si dos peticiones venden entradas a la vez, ambas leen el fichero, ambas modifican su copia en memoria y ambas lo reescriben entero. La última escritura gana y la primera venta desaparece. No hay bloqueo, no hay arbitraje, no hay nadie que decida el orden. Es exactamente el escenario de sobreventa.
- Escrituras no atómicas. Un
writeFilede un catálogo completo puede interrumpirse (fallo de disco,SIGKILL, contenedor reiniciado) dejando un JSON truncado. Un JSON truncado no es un catálogo con un evento menos: es un fichero que ya no parsea, y la aplicación no arranca. - Todo el catálogo en memoria. Con 3 eventos y 7 sesiones es gratis. Con 40.000 eventos históricos, cada proceso Node carga megabytes que nunca consulta. La memoria de un proceso es un recurso caro y compartido con el bucle de eventos.
- Sin consultas eficientes. "Dame las sesiones del Teatro Almendra entre el 1 y el 15 de marzo con entradas libres" hoy se resuelve recorriendo el array entero en JavaScript. Es O(n) sobre todo el conjunto de datos, en el hilo principal, y no hay forma de mejorarlo sin escribir tú mismo un índice.
- Sin integridad. Nada impide que un pedido apunte a
ses-999-9, una sesión que no existe. Nada impide quevendidassupere aaforo. Las invariantes viven solo en las clases del dominio, y si alguien escribe el JSON a mano o un script se salta el dominio, quedan datos corruptos para siempre. - Sin historial. El fichero solo tiene el estado actual. ¿Cuántas entradas se vendieron el martes pasado? ¿Quién anuló el pedido? No hay respuesta: el estado anterior se sobrescribió.
- Sin acceso desde varios procesos. Este es el que más duele de cara al futuro. En el módulo 10 veremos
clustery arrancaremos varios procesos Node para aprovechar todos los núcleos de la máquina. Con un fichero JSON, cada proceso tendría su propia caché en memoria y su propia idea del aforo; ni siquiera se enterarían de las ventas de los demás. La persistencia en fichero impide escalar. Y en el módulo 11, al desplegar con PM2 o varias réplicas en contenedores, el problema se multiplica por el número de instancias.
La conclusión no es que los ficheros sean malos. Un fichero JSON es perfecto para configuración, para semillas, para exportar un informe. Es malo como almacén de estado mutable compartido y concurrente. Y eso es precisamente lo que es el aforo de una sesión.
Qué aporta realmente un SGBD
Un sistema gestor de bases de datos (SGBD) es un proceso especializado, normalmente en otra máquina o en otro contenedor, cuyo único trabajo es custodiar datos. A cambio de aprenderlo y de operarlo, te da seis cosas que tú no vas a reimplementar bien:
| Garantía | Qué significa | Qué resuelve en Escena Viva |
|---|---|---|
| Persistencia duradera | Una escritura confirmada sobrevive a un corte de luz, gracias al registro de escritura anticipada (WAL/journal) | Nunca se pierde una venta ya cobrada |
| Concurrencia controlada | Bloqueos y control de versiones para que N clientes escriban a la vez sin corromper nada | Dos compradores simultáneos no se pisan |
| Lenguaje de consulta | Filtrar, ordenar, agrupar y agregar en el motor, no en tu proceso | Informes de recaudación sin leer todo el catálogo |
| Índices | Estructuras auxiliares que convierten búsquedas O(n) en O(log n) | Buscar por sala o por rango de fechas es instantáneo |
| Integridad | Restricciones que el motor hace cumplir pase lo que pase | vendidas <= aforo como ley física, no como buena intención |
| Transacciones | Varias operaciones que ocurren todas o ninguna | Reservar aforo + crear pedido + emitir entradas como un solo acto |
Fíjate en la última fila. Es la que resuelve el problema que arrastramos, y a ella le dedicaremos media lección 07-06.
Relacional frente a documental
Hay dos grandes familias que un desarrollador de Node se encuentra a diario. Ninguna es "moderna" y la otra "antigua"; ninguna es "seria" y la otra "de juguete". Son modelos con compromisos distintos.
| Criterio | Relacional (PostgreSQL, MySQL) | Documental (MongoDB) |
|---|---|---|
| Unidad de datos | Fila en una tabla, con columnas fijas | Documento BSON, parecido a un objeto JSON anidado |
| Esquema | Rígido y declarado; cambiarlo requiere migración | Flexible; el motor no exige forma, la impone tu ODM |
| Relaciones | Claves foráneas y JOIN resueltos por el motor |
Referencias resueltas con consultas extra, o datos incrustados |
| Integridad referencial | Sí, garantizada por el motor | No entre colecciones; la garantizas tú |
| Transacciones | Nativas, multitabla, desde siempre | Atómicas por documento; multidocumento desde la 4.0 con réplicas |
| Consultas complejas | SQL, muy expresivo y optimizado décadas | Framework de agregación, potente pero distinto |
| Escalado | Vertical y réplicas de lectura; particionado más costoso | Particionado horizontal (sharding) de serie |
| Casos ideales | Datos muy relacionados, informes, dinero, invariantes duras | Documentos autocontenidos, esquema cambiante, alto volumen de lectura |
La forma sana de leer esta tabla: elige el modelo que se parezca a tu patrón de acceso. Si tu aplicación casi siempre lee "un evento con todas sus sesiones", el documental te da eso en una sola lectura de disco. Si tu aplicación casi siempre cruza cinco entidades para sacar un informe, el relacional te lo da en una sola consulta optimizada.
Escena Viva, curiosamente, tiene las dos caras: el catálogo es documental por naturaleza (un evento contiene sus sesiones) y la venta es relacional por naturaleza (pedidos, entradas, usuarios y dinero). Por eso este módulo hace las dos cosas: MongoDB será la persistencia oficial en las lecciones 2 a 4, y en las lecciones 5 y 6 modelaremos lo mismo en PostgreSQL para ver qué cambia — y descubriremos que la sobreventa se resuelve de forma especialmente elegante allí.
ACID, BASE y el teorema CAP aplicados a un aforo
ACID describe las garantías de una transacción. Atomicidad: la transacción ocurre entera o no ocurre. Consistencia: al terminar, todas las reglas del esquema siguen cumpliéndose. Aislamiento: dos transacciones simultáneas no se ven a medias, el resultado es como si se hubieran ejecutado en algún orden. Durabilidad: una vez confirmada, sobrevive al fallo del servidor. Los sistemas relacionales nacieron con ACID y los documentales han ido incorporándolo. BASE es el compromiso contrario, típico de sistemas distribuidos masivos: Basically Available (siempre responde), Soft state (el estado puede estar en tránsito), Eventually consistent (si dejas de escribir, en algún momento todas las réplicas coincidirán). BASE cambia corrección inmediata por disponibilidad y escala.
El teorema CAP explica por qué existe ese cambio. En un sistema distribuido en el que la red puede partirse (y siempre puede), no se pueden mantener a la vez consistencia fuerte (C) y disponibilidad total (A): cuando dos nodos dejan de verse, o rechazas escrituras para no divergir, o las aceptas y divergirás. Como la tolerancia a particiones (P) no es opcional en una red real, la decisión práctica es entre CP (rechazo escrituras dudosas, sigo siendo correcto) y AP (acepto todo, ya reconciliaré).
| ACID | BASE | |
|---|---|---|
| Prioriza | Corrección inmediata | Disponibilidad y escala |
| Ante una partición de red | Rechaza escrituras dudosas (CP) | Las acepta y reconcilia después (AP) |
| Estado tras escribir | Definitivo y visible para todos | Puede tardar en propagarse |
| Ejemplo en Escena Viva | vendidas de una sesión, estado del pedido |
Contador de visitas, recomendaciones |
Aplicado a Escena Viva: un aforo exige garantías fuertes. Vender una entrada de más no es un detalle cosmético que se arregle "eventualmente"; es una persona con un código EV-2026-000431 de pie en la puerta del Teatro Almendra sin butaca, y un reembolso, y una reclamación. El contador vendidas es el ejemplo de libro de dato que necesita consistencia inmediata: preferimos rechazar una compra dudosa (CP) a aceptarla y descubrir después que no había sitio. En cambio, otros datos de la misma plataforma toleran perfectamente consistencia eventual: el número de visitas de la página de un evento, las recomendaciones, o la caché del catálogo que veremos con Redis en el módulo 10. La garantía se elige por dato, no por aplicación.
El panorama de bases de datos para Node.js
| Motor | Familia | Punto fuerte | Cuándo elegirlo | Paquete npm |
|---|---|---|---|---|
| PostgreSQL | Relacional | El más completo: JSONB, tipos ricos, transacciones sólidas, extensiones | Por defecto si dudas y hay dinero o invariantes de por medio | pg |
| MySQL / MariaDB | Relacional | Enorme despliegue, operación conocida, muy rápido en lecturas simples | Ecosistema o hosting que ya lo impone | mysql2 |
| SQLite | Relacional embebida | Sin servidor: la base de datos es un fichero | Aplicaciones de escritorio, CLIs, pruebas, prototipos | better-sqlite3 |
| MongoDB | Documental | Documentos anidados, esquema flexible, sharding de serie | Datos autocontenidos y con forma cambiante | mongodb |
| Redis | Clave-valor en memoria | Latencia de microsegundos, estructuras de datos, TTL | Caché, sesiones, colas — es del módulo 10, no un almacén principal | redis |
Redis merece una aclaración porque se malinterpreta mucho: no es "una base de datos más rápida", es una base de datos en memoria con un modelo de durabilidad distinto. Se usa delante de la base de datos principal, no en su lugar. En el módulo 10 la usaremos para cachear el catálogo y para encolar los correos de confirmación.
Controlador nativo frente a ORM/ODM
Entre tu código y el motor hay siempre un controlador (driver): habla el protocolo binario de la base de datos y expone una API de bajo nivel. Encima puede haber un ORM (Object-Relational Mapper, mundo SQL) o un ODM (Object-Document Mapper, mundo documental), que traduce entre filas o documentos y objetos de JavaScript.
| Aspecto | Controlador nativo | ORM / ODM |
|---|---|---|
| Control sobre la consulta | Total, escribes exactamente lo que se ejecuta | Indirecto: la genera la biblioteca |
| Rendimiento máximo | El techo del motor | Un poco menos, por la capa de traducción |
| Esquema y validación | La escribes tú | Declarativa, incluida |
| Relaciones | Las resuelves a mano | populate / include |
| Migraciones | Tuyas | Herramienta integrada |
| Curva de aprendizaje | Aprendes el motor | Aprendes el motor y la biblioteca |
| Riesgo | Código repetitivo, errores manuales | Consultas ineficientes que no ves, "magia" difícil de depurar |
Lo que te da un ORM/ODM: menos código repetitivo, validación declarativa, tipos coherentes, hooks, migraciones y un modelo mental uniforme para todo el equipo. Lo que te quita: transparencia. Una línea inocente puede generar 200 consultas (el problema N+1 que veremos en la lección 07-04). La regla profesional es: usa el ORM para el 95 % del código y no tengas miedo de bajar a consultas en crudo para el 5 % que importa, midiendo antes de decidir. En este módulo usaremos Mongoose (ODM) y Sequelize (ORM), y en ambos casos veremos cómo escapar a la consulta cruda.
Diseñar el modelo de datos de Escena Viva
Antes de escribir un esquema hay que decidir qué es una entidad. Una entidad es algo que tiene identidad propia y ciclo de vida propio: existe antes y después de la operación que la creó, y tiene sentido buscarla por sí misma. Un evento es una entidad. Un pedido es una entidad. El nombre de la sala, en cambio, es un atributo.
La segunda decisión es qué se agrupa y qué se separa. En el mundo documental esto se llama incrustar frente a referenciar y lo estudiaremos a fondo en la lección 07-04, pero el criterio ya podemos aplicarlo con dos preguntas:
- ¿Se leen siempre juntos? Si nunca pides una sesión sin su evento, agrúpalos.
- ¿Crece sin límite? Si la colección hija crece indefinidamente, sepárala.
Apliquémoslo a nuestras entidades:
Eventocon sus sesiones incrustadas. Un evento tiene entre 1 y 7 sesiones en nuestro catálogo; una obra en cartel podría tener 60. Es un número acotado y conocido de antemano. Además, la pantalla de detalle del evento y la propia API (GET /eventos/:id) las piden siempre juntas: ya hoy el controladorlistarSesionesDelEventoparte de unEventocargado. Y su tamaño es minúsculo. Se incrustan.Pedidocomo colección aparte. Los pedidos crecen sin techo: un evento popular puede acumular decenas de miles. Tienen ciclo de vida propio (pendiente → pagado → emitido | anulado) y se consultan por su cuenta ("mis pedidos"). Meterlos dentro del evento haría crecer el documento hasta reventar el límite y obligaría a reescribirlo entero en cada compra. Se separan.Entradacomo colección aparte. Cada entrada tiene su código únicoEV-2026-000431, su estado (valida → usada | anulada) y se valida individualmente en la puerta, escaneando. Es la unidad de acceso más pequeña del sistema y la de mayor volumen. Se separa, con referencias al pedido y a la sesión.Usuariocomo colección aparte, con surol(asistente, organizador, administrador). En este módulo lo creamos sin contraseñas ni autenticación: eso es íntegramente el módulo 8. Aquí solo dejamos el hueco preparado.
Y una tercera decisión, deliberadamente incómoda: el contador vendidas vive dentro de la sesión, aunque podría calcularse contando entradas válidas. Es una desnormalización a propósito, para que comprobar el aforo sea una lectura de un campo y no una agregación. El precio es mantener la coherencia entre el contador y las entradas reales; ese precio lo pagamos con las técnicas de la lección 07-06.
El modelo definitivo en un diagrama
erDiagram
USUARIO ||--o{ PEDIDO : "realiza"
EVENTO ||--|{ SESION : "contiene (incrustada)"
PEDIDO ||--|{ ENTRADA : "agrupa"
SESION ||--o{ ENTRADA : "es reservada por"
USUARIO {
string _id
string email
string nombre
string rol
}
EVENTO {
string _id
string eventoId
string titulo
string sala
string organizadorId
string categoria
string estado
int duracionMinutos
}
SESION {
string sesionId
date fechaHora
int aforo
int vendidas
int precioCentimos
}
PEDIDO {
string _id
string usuarioId
string sesionId
int cantidad
int totalCentimos
string estado
string canal
}
ENTRADA {
string _id
string codigo
string pedidoId
string sesionId
string estado
}
Léelo así: SESION aparece como entidad en el diagrama porque conceptualmente lo es, pero físicamente vive dentro del documento EVENTO como subdocumento. PEDIDO y ENTRADA son colecciones independientes que se relacionan por referencia. Este es el plano que implementaremos en la lección 07-02 con Mongoose y que traduciremos a tablas en la 07-05 con Sequelize — donde SESION sí será una tabla propia, y veremos por qué.
Aislar la persistencia: el patrón repositorio
Aquí va la advertencia de arquitectura más importante del módulo. Si los controladores llaman directamente a Evento.find(...), tu aplicación queda casada con MongoDB para siempre. Cambiar de motor, o probar con datos falsos, o consultar dos fuentes distintas, exigiría reescribir todos los controladores.
La solución es una capa fina: src/repositorios/. Un repositorio expone operaciones en el lenguaje del negocio (obtenerCatalogo, obtenerEventoPorId, reservarAforo, crearPedido) y esconde por completo cómo se cumplen. Por encima de él, controladores y dominio no saben si debajo hay Mongo, Postgres o un array.
// src/repositorios/eventos.js (contrato, la implementación llega en la lección 07-03)
'use strict';
/**
* Devuelve el catálogo completo como instancias del dominio.
* Quien llama no sabe si viene de Mongo, de Postgres o de un fichero.
*/
async function obtenerCatalogo() {
throw new Error('sin implementar');
}
/** Devuelve un Evento del dominio, o null si no existe. */
async function obtenerEventoPorId(eventoId) {
throw new Error('sin implementar');
}
module.exports = { obtenerCatalogo, obtenerEventoPorId };Fíjate en el detalle decisivo: es exactamente la firma pública de src/catalogo-datos.js. Por eso podremos sustituir el módulo entero sin tocar src/controladores/eventos.js. Y por eso, en la lección 07-05, escribiremos una segunda implementación sobre Sequelize sin cambiar una línea de la aplicación. El repositorio devuelve objetos del dominio (Evento, Sesion), no documentos de Mongoose: así el dominio conserva sus getters (ocupacion, agotado) y sus invariantes, tal como los dejamos en el módulo 2.
Instalar MongoDB y comprobar la conexión
Tres caminos, todos válidos. Elige uno.
Opción A: servicio local. Instala MongoDB Community Server desde el sitio oficial y arráncalo como servicio del sistema:
# Comprobar que el servicio está vivo (Linux con systemd)
sudo systemctl status mongod
sudo systemctl start mongodOpción B: Docker. La más limpia porque no ensucia tu máquina y se borra en un comando. En el módulo 11 formalizaremos esto con docker compose:
# Levanta MongoDB 7 en el puerto estándar, con volumen persistente
docker run -d --name mongo-escena-viva \
-p 27017:27017 \
-v escena-viva-datos:/data/db \
mongo:7Opción C: MongoDB Atlas. El servicio gestionado en la nube. Tiene una capa gratuita suficiente para este curso; te dará una URL del tipo mongodb+srv://usuario:[email protected]/escena_viva. Es lo que usarás en producción si no quieres operar el motor tú mismo.
Sea cual sea la opción, comprueba la conexión con el cliente oficial mongosh:
Y dentro del intérprete:
// Ver en qué base estamos (la crea al primer escrito, no antes)
db.getName(); // 'escena_viva'
// Insertar un documento de prueba y recuperarlo
db.prueba.insertOne({ sala: 'Teatro Almendra', creado: new Date() });
db.prueba.find();
// Limpiar
db.prueba.drop();Una peculiaridad que sorprende: MongoDB no crea la base de datos ni la colección hasta el primer documento. Si show dbs no lista escena_viva recién conectado, no es un error.
La URL de conexión no se escribirá nunca a mano en el código. Irá en .env y se leerá desde src/config/index.js, que ya es el único punto de la aplicación que toca process.env:
Y con la conexión comprobada, conviene fijar de antemano qué preguntas hará la aplicación, porque el modelo de datos se diseña desde las consultas hacia atrás. Estas son las cinco consultas más frecuentes de Escena Viva, y son las que gobernarán los índices de la lección 07-02:
| Consulta | Frecuencia | Entidad de partida |
|---|---|---|
| Catálogo de eventos publicados | Muy alta | Evento |
| Detalle de un evento con sus sesiones | Muy alta | Evento |
| Comprobar el aforo libre de una sesión | Muy alta (crítica) | Sesión dentro del Evento |
| Pedidos de un usuario | Media | Pedido |
| Validar una entrada por su código en la puerta | Alta en horario de función | Entrada |
Errores Comunes y Consejos
- Elegir el motor por moda. "Usamos Mongo porque es JavaScript" no es un criterio. El criterio es el patrón de acceso, las garantías que necesitas y lo que tu equipo sabe operar.
- Creer que "sin esquema" significa "sin diseño". MongoDB no te obliga a declarar una forma, pero tus datos la tienen igual. Si no la decides tú, la decidirá cada
insertque escribas, y acabarás con siete variantes del mismo documento. - Meter la URL de conexión en el código. Con credenciales dentro y subida a git. Va en la configuración, siempre. Ya tienes el sitio:
src/config/index.js. - Dejar la base de datos sin autenticación "porque es desarrollo". Un MongoDB expuesto en el 27017 sin usuario es de los blancos favoritos de los escáneres automáticos. En local, al menos no publiques el puerto fuera de
localhost. - Consejo: antes de modelar, escribe las cinco consultas que tu aplicación hará más veces. El modelo de datos se diseña desde las consultas hacia atrás, no desde las entidades hacia delante.
- Consejo: conserva
datos/eventos.json. No lo borraremos: en la lección 07-06 se convertirá en la semilla que carga la base de datos. Se jubila como almacén, no como fuente.
Ejercicios
Ejercicio 1: auditoría de tu propio código
Abre src/catalogo-datos.js y src/controladores/pedidos.js y localiza, sin ejecutar nada, los puntos exactos donde dos peticiones simultáneas pueden corromper el estado. Escribe para cada uno: qué línea lee, qué línea escribe, y qué pasa si otra petición se cuela entre ambas.
Ejercicio 2: decidir el modelo
Escena Viva quiere añadir dos cosas: (a) las reseñas que los asistentes dejan sobre un evento, y (b) el plano de butacas de cada sala. Para cada una, decide si se incrusta o se separa y justifica la decisión con los dos criterios de esta lección (lectura conjunta y crecimiento).
Ejercicio 3: elegir garantías
Clasifica estos cuatro datos según necesiten consistencia fuerte o eventual, y justifica: (1) vendidas de una sesión, (2) el contador de visitas de la ficha de un evento, (3) el estado de un pedido, (4) el listado de "eventos recomendados para ti".
Soluciones
Ejercicio 1. El patrón peligroso es siempre leer-modificar-escribir sin exclusión mutua. En el flujo de compra: se lee el catálogo memorizado, se comprueba sesion.libres >= cantidad, se llama a sesion.vender() y se persiste. Si dos peticiones ejecutan la comprobación antes de que cualquiera de las dos escriba, ambas la superan aunque solo quede una entrada. La memorización empeora las cosas: la copia en memoria puede llevar minutos desactualizada respecto al fichero. Y como Node es monohilo, el hueco se abre exactamente en cada await — el bucle de eventos del módulo 2 cede el turno justo ahí.
Ejercicio 2. (a) Las reseñas se separan: crecen sin límite (un festival puede acumular miles), no se necesitan para renderizar el catálogo, se paginan y se ordenan por fecha, y se moderan con ciclo de vida propio. (b) El plano de butacas se incrusta en la sala (o se referencia como catálogo aparte si varias salas comparten plantilla), porque es de tamaño acotado, cambia casi nunca y siempre se lee junto con la sala. Ojo: la ocupación de cada butaca en una sesión concreta sí es volátil y de alta escritura, así que ese estado no debe vivir en el plano estático.
Ejercicio 3. (1) vendidas: fuerte, es la definición misma del problema de sobreventa. (3) Estado del pedido: fuerte, decide si se cobra y si se emiten entradas; una lectura obsoleta puede duplicar un cobro. (2) Visitas: eventual, un error de un 2 % durante unos segundos no perjudica a nadie y a cambio permite contar en memoria y volcar por lotes. (4) Recomendaciones: eventual, se calculan en diferido y nadie nota que llevan una hora de retraso.
Conclusión
Hemos cerrado la etapa del fichero. Sabes ahora, con nombres concretos, qué le falta a datos/eventos.json: atomicidad, concurrencia, consultas, índices, integridad, historial y acceso multiproceso. Sabes qué te da un SGBD a cambio, qué separa al modelo relacional del documental sin necesidad de tomar partido, y por qué un aforo pertenece al territorio de las garantías fuertes según ACID y CAP. Has visto el panorama real de motores para Node y el compromiso entre bajar al controlador nativo o apoyarte en un ORM/ODM. Y, sobre todo, tienes el modelo de datos de Escena Viva decidido y razonado: Evento con sus sesiones incrustadas, Pedido, Entrada y Usuario como colecciones separadas, con el contador vendidas desnormalizado a propósito.
También tienes la pieza de arquitectura que sostiene todo el módulo: src/repositorios/, la frontera que impedirá que el motor de base de datos se filtre a los controladores y al dominio.
En la lección siguiente bajamos al código. Conectaremos con MongoDB desde src/config/index.js, escribiremos src/db/conexion.js con su apertura al arrancar y su cierre en el apagado ordenado que ya existe en src/servidor.js, y traduciremos este diagrama a esquemas y modelos de Mongoose en src/modelos/: tipos, validadores, virtuals, middleware de esquema e índices. El plano se convierte en estructura.
Curso de Node.js: De Principiante a Avanzado
Módulo 1: Introducción a Node.js
- ¿Qué es Node.js?
- Instalación y Configuración del Entorno
- Tu Primer Programa en Node.js
- El REPL de Node.js
- JavaScript Moderno para Node.js
- El Proyecto del Curso: la Plataforma Escena Viva
Módulo 2: Conceptos Básicos
- Arquitectura de Node.js
- El Bucle de Eventos (Event Loop)
- Callbacks y Programación Asíncrona
- Promesas y async/await
- Eventos y EventEmitter
- Módulos CommonJS y require()
- Módulos ES e Interoperabilidad
Módulo 3: Sistema de Archivos y E/S
- Lectura y Escritura de Archivos
- El Módulo fs a Fondo
- Rutas Multiplataforma con el Módulo path
- Trabajando con Streams
- Streams de Transformación y pipeline
- Buffers y Datos Binarios
Módulo 4: HTTP y Servidores Web
- Creando un Servidor HTTP Simple
- Manejo de Solicitudes y Respuestas
- Enrutamiento Manual
- Sirviendo Archivos Estáticos
- Recibiendo Datos: Cuerpos de Petición y JSON
- Consumiendo APIs Externas desde Node.js
Módulo 5: NPM y Gestión de Paquetes
- Introducción a NPM y package.json
- Instalación y Uso de Paquetes
- Versionado Semántico y package-lock
- Scripts de npm y Automatización del Proyecto
- Creación y Publicación de Paquetes
- Seguridad y Mantenimiento de Dependencias
Módulo 6: Framework Express.js
- Introducción a Express.js
- Configuración de una Aplicación Express
- Enrutamiento en Express
- Middleware
- Middleware de Terceros Esenciales
- Validación de Datos de Entrada
- Manejo de Errores
Módulo 7: Bases de Datos y ORMs
- Introducción a las Bases de Datos
- Usando MongoDB con Mongoose
- Operaciones CRUD
- Relaciones, Poblado y Consultas Avanzadas
- Usando Bases de Datos SQL con Sequelize
- Migraciones, Transacciones y Datos de Prueba
Módulo 8: Autenticación y Autorización
- Introducción a la Autenticación
- Registro de Usuarios y Hash de Contraseñas
- Sesiones y Cookies con Passport.js
- Autenticación con JWT
- Control de Acceso Basado en Roles
- Buenas Prácticas de Seguridad en APIs
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con Mocha y Chai
- Dobles de Prueba con Sinon
- Pruebas de Integración
- Cobertura y Automatización de las Pruebas
- Depuración de Aplicaciones Node.js
Módulo 10: Temas Avanzados
- El Módulo Cluster
- Hilos de Trabajo (Worker Threads)
- Caché y Colas de Trabajo con Redis
- Optimización del Rendimiento
- Construcción de APIs RESTful
- GraphQL con Node.js
Módulo 11: Despliegue y DevOps
- Configuración y Variables de Entorno
- Registro y Monitorización en Producción
- Usando PM2 para la Gestión de Procesos
- Empaquetado con Docker
- Desplegando en Heroku y Otras PaaS
- Integración y Despliegue Continuos
