14-Versión con Modelo Separado
Versión con Modelo Separado (MVC completo)
Voy a reorganizar la aplicación para separar claramente el Modelo, Vista y Controlador. Aquí está la estructura actualizada:
mi-app/
├── config/
│ └── database.js
├── controllers/
│ └── tareasController.js
├── models/
│ └── tareaModel.js
├── public/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── app.js
├── router/
│ └── tareas.js
├── views/
│ ├── index.ejs
│ └── partials/
│ ├── header.ejs
│ └── footer.ejs
├── app.js
└── package.json
1. Configuración de la base de datos (config/database.js)
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root', // Cambia por tu usuario
password: '', // Cambia por tu contraseña
database: 'lista_tareas',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports = pool;
2. Modelo (models/tareaModel.js)
const pool = require('../config/database');
class Tarea {
// Obtener todas las tareas
static async getAll() {
const [rows] = await pool.query('SELECT * FROM tareas ORDER BY creado_en DESC');
return rows;
}
// Crear nueva tarea
static async create(nombre) {
await pool.query('INSERT INTO tareas (nombre) VALUES (?)', [nombre]);
}
// Eliminar tarea
static async delete(id) {
await pool.query('DELETE FROM tareas WHERE id = ?', [id]);
}
}
module.exports = Tarea;
3. Controlador (controllers/tareasController.js)
const Tarea = require('../models/tareaModel');
exports.mostrarTareas = async (req, res) => {
try {
const tareas = await Tarea.getAll();
res.render('index', { tareas });
} catch (error) {
console.error(error);
res.status(500).send('Error al cargar las tareas');
}
};
exports.agregarTarea = async (req, res) => {
try {
const { nombre } = req.body;
if (nombre && nombre.trim() !== '') {
await Tarea.create(nombre.trim());
}
res.redirect('/');
} catch (error) {
console.error(error);
res.status(500).send('Error al agregar la tarea');
}
};
exports.eliminarTarea = async (req, res) => {
try {
const { id } = req.params;
await Tarea.delete(id);
res.redirect('/');
} catch (error) {
console.error(error);
res.status(500).send('Error al eliminar la tarea');
}
};
4. Router (router/tareas.js)
const express = require('express');
const router = express.Router();
const tareasController = require('../controllers/tareasController');
// Ruta principal - muestra todas las tareas
router.get('/', tareasController.mostrarTareas);
// Ruta para agregar tarea
router.post('/agregar', tareasController.agregarTarea);
// Ruta para eliminar tarea
router.post('/eliminar/:id', tareasController.eliminarTarea);
module.exports = router;
5. Archivo principal (app.js)
const express = require('express');
const path = require('path');
const app = express();
// Configuración de EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Middleware para archivos estáticos
app.use(express.static(path.join(__dirname, 'public')));
// Middleware para parsear datos del formulario
app.use(express.urlencoded({ extended: true }));
// Rutas
const tareasRouter = require('./router/tareas');
app.use('/', tareasRouter);
// Puerto
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Servidor corriendo en http://localhost:${PORT}`);
});
Explicación de la separación MVC
Modelo (models/tareaModel.js):
Contiene toda la lógica de acceso a la base de datos.
Define una clase
Tarea
con métodos estáticos para cada operación.Se encarga de interactuar directamente con MySQL.
No sabe nada de Express o rutas HTTP.
Vista (views/):
Sigue igual que antes, con plantillas EJS.
Solo muestra datos, no contiene lógica de negocio.
Controlador (controllers/tareasController.js):
Maneja las peticiones HTTP.
Llama a los métodos del modelo cuando necesita datos.
Decide qué vista renderizar o a qué ruta redirigir.
No interactúa directamente con la base de datos.
Router (router/tareas.js):
Mapea las rutas URL a los métodos del controlador.
Mantiene limpia la organización de las rutas.
Configuración (config/database.js):
Centraliza la configuración de la conexión a la base de datos.
Facilita cambios futuros en la configuración.
Beneficios de esta estructura
Separación de preocupaciones: Cada componente tiene una responsabilidad clara.
Mantenibilidad: Es más fácil hacer cambios sin afectar otras partes.
Testabilidad: Puedes probar cada componente por separado.
Escalabilidad: Facilita agregar nuevas características.
Reusabilidad: El modelo puede ser usado por diferentes controladores.
Cómo extender esta aplicación
Para agregar más funcionalidades (como editar tareas):
Añade métodos en el modelo (
Tarea.update()
)Crea nuevas acciones en el controlador
Agrega las rutas correspondientes
Crea/actualiza las vistas necesarias
Para agregar más modelos (como usuarios):
Crea un nuevo archivo en
models/
Crea su controlador correspondiente
Configura sus rutas
Esta estructura sigue las mejores prácticas para aplicaciones Node.js y te prepara para crecer tu aplicación de manera organizada
Comentarios
Publicar un comentario