Versión MVC SIN routes
Versión MVC SIN routes (todo en app.js)
Aquí tienes el ejercicio refactorizado a MVC sin carpeta de rutas:
📁 ESTRUCTURA DEL PROYECTO
mi-app-mvc/ ├── models/ │ └── Producto.js ├── controllers/ │ └── productoController.js ├── views/ │ ├── index.ejs │ └── productos.ejs ├── public/ │ └── (archivos estáticos) └── app.js
📄 CÓDIGO COMPLETO
1️⃣ models/Producto.js (Modelo - Datos)
// Modelo: Maneja toda la lógica de datos class Producto { constructor() { this.productos = [ { id: 1, nombre: 'Laptop', precio: 1000 }, { id: 2, nombre: 'Mouse', precio: 25 }, { id: 3, nombre: 'Teclado', precio: 50 } ]; this.contadorId = 4; } // Obtener todos los productos obtenerTodos() { return this.productos; } // Obtener producto por ID obtenerPorId(id) { return this.productos.find(p => p.id === id); } // Crear nuevo producto crear(nombre, precio) { const nuevoProducto = { id: this.contadorId++, nombre: nombre.trim(), precio: parseFloat(precio) }; this.productos.push(nuevoProducto); return nuevoProducto; } // Obtener cantidad total obtenerTotal() { return this.productos.length; } } module.exports = new Producto();
2️⃣ controllers/productoController.js (Controlador - Lógica)
const Producto = require('../models/Producto'); class ProductoController { // GET / - Renderizar vista principal (index) mostrarInicio(req, res) { const productos = Producto.obtenerTodos(); res.render('index', { title: 'Mi Tienda', productos: productos }); } // GET /productos-vista - Renderizar vista de productos mostrarProductosVista(req, res) { const productos = Producto.obtenerTodos(); const total = Producto.obtenerTotal(); res.render('productos', { title: 'Lista de Productos', productos: productos, total: total }); } // GET /productos - API: Devolver todos (JSON) obtenerTodosAPI(req, res) { const productos = Producto.obtenerTodos(); res.json(productos); } // GET /productos/:id - API: Devolver uno (JSON) obtenerUnoAPI(req, res) { const id = parseInt(req.params.id); const producto = Producto.obtenerPorId(id); if (!producto) { return res.status(404).json({ error: 'Producto no encontrado' }); } res.json(producto); } // POST /productos-formulario - Procesar formulario procesarFormulario(req, res) { const { nombre, precio } = req.body; // Validaciones if (!nombre || !precio) { return res.status(400).send(` <h1>Error</h1> <p>Faltan datos: nombre y precio son obligatorios</p> <a href="/">Volver al inicio</a> `); } if (nombre.trim().length < 3) { return res.status(400).send(` <h1>Error</h1> <p>El nombre debe tener al menos 3 caracteres</p> <a href="/">Volver al inicio</a> `); } if (isNaN(precio) || parseFloat(precio) <= 0) { return res.status(400).send(` <h1>Error</h1> <p>El precio debe ser un número mayor a 0</p> <a href="/">Volver al inicio</a> `); } // Crear producto const nuevoProducto = Producto.crear(nombre, precio); // Redirigir a la página principal res.redirect('/'); } } module.exports = new ProductoController();
3️⃣ app.js (Configuración principal SIN router)
const express = require('express'); const path = require('path'); const productoController = require('./controllers/productoController'); const app = express(); const PORT = 3000; // ============ CONFIGURACIÓN EJS ============ app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); // ============ MIDDLEWARES ============ app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static('public')); app.use('/css', express.static('node_modules/bootstrap/dist/css')); app.use('/js', express.static('node_modules/bootstrap/dist/js')); // ============ RUTAS (usando el controlador directamente) ============ // Rutas Web (EJS) app.get('/', productoController.mostrarInicio); app.get('/productos-vista', productoController.mostrarProductosVista); // Rutas API (JSON) app.get('/productos', productoController.obtenerTodosAPI); app.get('/productos/:id', productoController.obtenerUnoAPI); // Ruta para formulario app.post('/productos-formulario', productoController.procesarFormulario); // ============ INICIAR SERVIDOR ============ app.listen(PORT, () => { console.log(`=================================`); console.log(`🚀 Servidor MVC corriendo en http://localhost:${PORT}`); console.log(`=================================`); console.log(`📌 Vistas EJS:`); console.log(` 🏠 Inicio: http://localhost:${PORT}/`); console.log(` 📦 Productos: http://localhost:${PORT}/productos-vista`); console.log(``); console.log(`📌 API JSON:`); console.log(` GET /productos`); console.log(` GET /productos/1`); console.log(``); console.log(`📌 Formulario (POST):`); console.log(` POST /productos-formulario`); console.log(`=================================`); });
4️⃣ views/index.ejs (Vista principal)
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= title %></title> <link rel="stylesheet" href="/css/bootstrap.min.css"> </head> <body> <div class="container mt-5"> <h1 class="text-center">📦 <%= title %></h1> <div class="row mt-4"> <div class="col-md-8 offset-md-2"> <div class="card"> <div class="card-header bg-primary text-white"> <h4 class="mb-0">Bienvenido a tu Tienda</h4> </div> <div class="card-body"> <p>Gestiona tus productos fácilmente</p> <div class="row"> <div class="col-md-6"> <div class="card text-center"> <div class="card-body"> <h5 class="card-title">📋 Ver Productos</h5> <p class="card-text">Consulta todos los productos disponibles</p> <a href="/productos-vista" class="btn btn-primary">Ver Productos</a> </div> </div> </div> <div class="col-md-6"> <div class="card text-center"> <div class="card-body"> <h5 class="card-title">➕ API REST</h5> <p class="card-text">Accede a los datos via JSON</p> <a href="/productos" class="btn btn-success">Ver API</a> </div> </div> </div> </div> </div> </div> </div> </div> <!-- Formulario para agregar productos --> <div class="row mt-4"> <div class="col-md-8 offset-md-2"> <div class="card"> <div class="card-header bg-success text-white"> <h4 class="mb-0">➕ Agregar Nuevo Producto</h4> </div> <div class="card-body"> <form action="/productos-formulario" method="POST"> <div class="mb-3"> <label for="nombre" class="form-label">Nombre del producto</label> <input type="text" class="form-control" id="nombre" name="nombre" required> </div> <div class="mb-3"> <label for="precio" class="form-label">Precio</label> <input type="number" class="form-control" id="precio" name="precio" step="0.01" required> </div> <button type="submit" class="btn btn-success">💾 Guardar Producto</button> </form> </div> </div> </div> </div> <!-- Info de arquitectura --> <div class="row mt-4"> <div class="col-md-8 offset-md-2"> <div class="alert alert-info"> <strong>📐 Arquitectura MVC</strong><br> ✅ Modelo: Producto.js (datos)<br> ✅ Vista: index.ejs y productos.ejs (interfaz)<br> ✅ Controlador: productoController.js (lógica)<br> ✅ Sin carpeta routes (todo en app.js) </div> </div> </div> </div> <script src="/js/bootstrap.min.js"></script> </body> </html>
5️⃣ views/productos.ejs (Lista de productos)
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= title %></title> <link rel="stylesheet" href="/css/bootstrap.min.css"> </head> <body> <div class="container mt-5"> <h1 class="text-center">📦 <%= title %></h1> <div class="row mt-4"> <div class="col-md-8 offset-md-2"> <div class="card"> <div class="card-header bg-primary text-white d-flex justify-content-between align-items-center"> <h4 class="mb-0">Productos disponibles</h4> <span class="badge bg-light text-dark">Total: <%= total %></span> </div> <div class="card-body"> <% if (productos.length === 0) { %> <div class="alert alert-warning"> No hay productos disponibles </div> <% } else { %> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>Nombre</th> <th>Precio</th> <th>Acciones</th> </tr> </thead> <tbody> <% productos.forEach(producto => { %> <tr> <td><%= producto.id %></td> <td><%= producto.nombre %></td> <td>$<%= producto.precio %></td> <td> <a href="/productos/<%= producto.id %>" class="btn btn-sm btn-info">Ver JSON</a> </td> </tr> <% }) %> </tbody> </table> </div> <% } %> </div> </div> <div class="text-center mt-3"> <a href="/" class="btn btn-secondary">← Volver al inicio</a> <a href="/productos" class="btn btn-success">Ver API JSON</a> </div> </div> </div> </div> <script src="/js/bootstrap.min.js"></script> </body> </html>
🚀 CÓMO EJECUTAR
# 1. Instalar dependencias npm install express ejs bootstrap # 2. Crear estructura de carpetas mkdir models controllers views public # 3. Crear los archivos con el código anterior # 4. Ejecutar node app.js
🎯 ENDPOINTS DISPONIBLES
| Método | URL | Descripción | Tipo |
|---|---|---|---|
| GET | / | Página principal | Web |
| GET | /productos-vista | Lista de productos | Web |
| GET | /productos | Todos los productos | API JSON |
| GET | /productos/1 | Un producto específico | API JSON |
| POST | /productos-formulario | Crear producto | Formulario |
📊 COMPARATIVA: CON routes vs SIN routes
| Característica | CON routes | SIN routes |
|---|---|---|
| Archivos | 4 (models, controllers, views, routes) | 3 (models, controllers, views) |
| Complejidad | Media | Baja |
| Ideal para | Proyectos medianos/grandes | Proyectos pequeños |
| Mantenimiento | Más organizado | Más directo |
¡Listo! Ahora tienes MVC completo SIN carpeta de rutas 🎯
Comentarios
Publicar un comentario