El patrón de arquitectura MVC convierte el desarrollo de aplicaciones complejas en un proceso mucho más manejable. Permite a varios desarrolladores trabajar simultáneamente en la aplicación.

Cuando me entere por primera vez de los patrones de MVC, me intimidó toda la terminología. Y más aún cuando empece a aplicar estos conceptos a una aplicación real.

Al dar un paso atrás para centrarse en lo que MVC es y lo que puede lograr, es mucho más fácil entender y aplicar el patrón a cualquier aplicación web.

¿Que es MVC?

MVC significa modelo (model) vista (view) controlador (controller). Esto es lo que significan cada uno de esos componentes.

  • Modelo: El backend que contiene toda la lógica de datos
  • Vista: El frontend o interfaz gráfica de usuario (GUI)
  • Controlador: El cerebro de la aplicación que controla como se muestran los datos.
Gráfica de patrones de arquitectura MVC

El concepto de MVC fue introducido por primera vez por Trygve Reenskaug, quien lo propuso como una forma de desarrollar el GUI de aplicaciones de escritorio.

Hoy en día, el patrón MVC se utiliza para aplicaciones web modernas porque permite que la aplicación sea escalable, mantenible y fácil de expandir.

¿Por qué deberías usar MVC?

Tres palabras: separación de preocupaciones (separation of concerns), o SoC para abreviar.

El patrón MVC te ayuda a dividir el código frontend y backend en componentes separados. De esta manera, es mucho más fácil administrar y hacer cambios a cualquiera de los lados sin que interfieran entre sí.

Pero esto es más fácil decirlo que hacerlo, especialmente cuando varios desarrolladores necesitan actualizar, modificar o depurar una aplicación completada simultáneamente.

Cómo usar MVC

Para ilustrar mejor el patrón MVC, he incluido una aplicación web que muestra cómo funcionan todos estos conceptos.

La aplicación "My Car Clicker" es una variación de una aplicación conocida como "Cat Clicker".

Estas son algunas de las principales diferencias en mi aplicación:

  1. No hay gatos, solo imágenes de carros potentes (¡lo siento, amantes de los gatos!)
  2. Se enumeran varios modelos de automóviles
  3. Hay varios contadores de clics
  4. Solo muestra el coche seleccionado

Ahora vamos a sumergirnos en estos tres componentes que componen el patrón de arquitectura MVC.

Modelo (datos)

El trabajo del modelo es simplemente administrar los datos. Ya sea que los datos provengan de una base de datos, una API o un objeto JSON, el modelo es responsable de administrarlos.

En la aplicación Car Clicker, el objeto modelo contiene un arreglo de objetos car con toda la información (datos) necesaria para la aplicación.

También gestiona el carro actual que se muestra con una variable que se establece inicialmente en null.

const model = {
    currentCar: null,
    cars: [
        {
            clickCount: 0,
            name: 'Coupe Maserati',
            imgSrc: 'img/black-convertible-coupe.jpg',
        },
        {
            clickCount: 0,
            name: 'Camaro SS 1LE',
            imgSrc: 'img/chevrolet-camaro.jpg',
        },
        {
            clickCount: 0,
            name: 'Dodger Charger 1970',
            imgSrc: 'img/dodge-charger.jpg',
        },
        {
            clickCount: 0,
            name: 'Ford Mustang 1966',
            imgSrc: 'img/ford-mustang.jpg',
        },
        {
            clickCount: 0,
            name: '190 SL Roadster 1962',
            imgSrc: 'img/mercedes-benz.jpg',
        },
    ],
};

Vistas (UI)

El trabajo de la vista es decidir qué verá el usuario en su pantalla y cómo.

La aplicación "Car Clicker" tiene dos vistas: carListView y CarView.

Ambas vistas tienen dos funciones críticas que definen lo que cada vista quiere inicializar y renderizar.

Estas funciones son donde la aplicación decide lo que el usuario verá y cómo.

carListView

const carListView = {
    init() {
        // almacene el elemento DOM para un fácil acceso más tarde
        this.carListElem = document.getElementById('car-list');

        // renderizar esta vista (actualizar los elementos DOM con los valores correctos)
        this.render();
    },

    render() {
        let car;
        let elem;
        let i;
        // obtener los carros para ser renderizados desde el controlador
        const cars = controller.getCars();

        // para asegurarse de que la lista está vacía antes de renderiz
        this.carListElem.innerHTML = '';

        // bucle sobre el arreglo de carros
        for(let i = 0; i < cars.length; i++) {
            // este es el carro que tenemos en bucle
            car = cars[i];

            // hacer un nuevo elemento de la lista de carros y establecer su texto
            elem = document.createElement('li');
            elem.className = 'list-group-item d-flex justify-content-between lh-condensed';
            elem.style.cursor = 'pointer';
            elem.textContent = car.name;
            elem.addEventListener(
                'click',
                (function(carCopy) {
                    return function() {
                        controller.setCurrentCar(carCopy);
                        carView.render();
                    };
                })(car)
            );
            // finalmente, agregua el elemento a la lista
            this.carListElem.appendChild(elem);
        }
    },
};

CarView

const carView = {
    init() {
        // almacene punteros a los elementos DOM para un fácil acceso más tarde
        this.carElem = document.getElementById('car');
        this.carNameElem = document.getElementById('car-name');
        this.carImageElem = document.getElementById('car-img');
        this.countElem = document.getElementById('car-count');
        this.elCount = document.getElementById('elCount');


        // al hacer clic, aumentar el contador del carro actual
        this.carImageElem.addEventListener('click', this.handleClick);

        // renderizar esta vista (actualizar los elementos DOM con los valores correctos)
        this.render();
    },

    handleClick() {
    	return controller.incrementCounter();
    },

    render() {
        // actualizar los elementos DOM con valores del carro actual
        const currentCar = controller.getCurrentCar();
        this.countElem.textContent = currentCar.clickCount;
        this.carNameElem.textContent = currentCar.name;
        this.carImageElem.src = currentCar.imgSrc;
        this.carImageElem.style.cursor = 'pointer';
    },
};

Controlador (Cerebro)

La responsabilidad del controlador es extraer, modificar y proporcionar datos al usuario. Esencialmente, el controlador es el enlace entre  y el modelo.

A través de las funciones getter y setter, el controlador extrae datos del modelo e inicializa las vistas.

Si hay alguna actualización desde las vistas, modifica los datos con una función setter.

const controller = {
    init() {
        // establecer el carro actual como el primero en la lista
        model.currentCar = model.cars[0];

        // indicar a las vistas que inicialicen
        carListView.init();
        carView.init();
    },

    getCurrentCar() {
    	return model.currentCar;
    },

    getCars() {
    	return model.cars;
    },

    // establecer el carro seleccionado actualmente en el objeto que se pasa en
    setCurrentCar(car) {
    	model.currentCar = car;
    },

    // incrementar el contador para el coche seleccionado actualmente
    incrementCounter() {
        model.currentCar.clickCount++;
        carView.render();
    },
};

controller.init();

MVC Frameworks

JavaScript ha crecido en popularidad, y se ha apoderado del backend en los últimos años. Cada vez más aplicaciones JavaScript  han optado por el patrón de arquitectura MVC de una manera u otra.

Los frameworks van y vienen, pero lo que ha sido constante son los conceptos tomados del patrón de arquitectura MVC.

Algunos de los primeros frameworks que aplicaron estos conceptos fueron KnockoutJS, Django y Ruby on Rails.

Conclusion

El concepto más atractivo del patrón MVC es la separación de preocupaciones.

Las aplicaciones web modernas son muy complejas, y hacer un cambio a veces puede ser un gran dolor de cabeza.

Administrar el frontend y el backend en componentes separados más pequeños permite que la aplicación sea escalable, mantenible y fácil de expandir.

**Si quieres echar un vistazo a la aplicación Car Clicker, el código está disponible en GitHub o echa un vistazo a la versión en vivo aquí.**

🌟 ¡Gracias por leer hasta aquí! 🌟