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:

text
Copy
Download
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)

javascript
Copy
Download
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)

javascript
Copy
Download
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)

javascript
Copy
Download
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)

javascript
Copy
Download
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)

javascript
Copy
Download
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

  1. 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.

  2. Vista (views/):

    • Sigue igual que antes, con plantillas EJS.

    • Solo muestra datos, no contiene lógica de negocio.

  3. 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.

  4. Router (router/tareas.js):

    • Mapea las rutas URL a los métodos del controlador.

    • Mantiene limpia la organización de las rutas.

  5. 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

  1. Separación de preocupaciones: Cada componente tiene una responsabilidad clara.

  2. Mantenibilidad: Es más fácil hacer cambios sin afectar otras partes.

  3. Testabilidad: Puedes probar cada componente por separado.

  4. Escalabilidad: Facilita agregar nuevas características.

  5. Reusabilidad: El modelo puede ser usado por diferentes controladores.

Cómo extender esta aplicación

  1. 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

  2. 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

Entradas más populares de este blog

9-Nuestra Primera Plantilla con EJS

6-Middleware?

Cómo Iniciar un Proyecto Node.js