Un catálogo, un carrito y una agenda pueden separar lo que la aplicación sabe, cómo lo presenta y cómo responde a las acciones del usuario. Los ejemplos siguientes muestran esa separación y qué ocurre cuando una entrada es inválida o una operación se rechaza.
MVC significa Model–View–Controller: modelo, vista y controlador. Es un patrón de organización del software, no un lenguaje ni un producto. El modelo reúne estado y comportamiento del dominio; la vista presenta información; el controlador interpreta la interacción y coordina la respuesta. [1][2]
10 ejemplos de MVC explicados
Son diseños didácticos con datos ficticios. En los casos web, el controlador coordina peticiones; en una interfaz de escritorio puede interpretar eventos de teclado o ratón. El modelo puede apoyarse en servicios de dominio y repositorios: no tiene que ser una sola clase ni una tabla.
EJEMPLO 01 · Lectura sin modificar datos
Consultar un producto en un catálogo
Acción: Una persona abre la ficha del producto 42.
Modelo: El catálogo busca el producto por su identificador y devuelve su nombre, precio y disponibilidad.
Controlador: Recibe el identificador de la ruta, solicita la búsqueda y elige la ficha o la respuesta de producto inexistente.
Vista: Presenta «Cuaderno · 3,50 € · Disponible», con el formato y las etiquetas de la página.
Resultado: Se muestra una ficha sin alterar existencias. En una aplicación web, un identificador que no existe puede producir una respuesta 404; una ficha vacía no equivale a un producto real.
Qué cuidar: La vista no ejecuta una consulta SQL para decidir qué producto es; trabaja con los datos preparados para mostrar.
EJEMPLO 02 · Consulta y resultado vacío
Filtrar tareas pendientes
Acción: El usuario selecciona el filtro «Pendientes».
Modelo: La colección contiene Leer (pendiente), Enviar (terminada) y Archivar (pendiente). La consulta selecciona las tareas no terminadas.
Controlador: Interpreta el filtro admitido y pide los resultados; no elimina las tareas que quedan fuera de la consulta.
Vista: Muestra Leer y Archivar. Si ninguna cumple el filtro, presenta «No hay tareas pendientes».
Resultado: Aparecen dos tareas de un total de tres. Cambiar el filtro cambia la presentación, no el estado de las tareas.
Qué cuidar: Un resultado vacío es una respuesta válida. Un fallo al leer los datos requiere otro tratamiento; no debe disfrazarse de lista vacía.
EJEMPLO 03 · Regla de negocio y cálculo
Añadir unidades a un carrito
Acción: El usuario pide tres unidades de un artículo de 12 €; la tienda permite un máximo de cinco por pedido.
Modelo: Comprueba cantidad y límite, conserva las líneas del carrito y calcula el subtotal: 3 × 12 = 36 €.
Controlador: Convierte la entrada de cantidad, invoca la operación del carrito y prepara la respuesta de éxito o error.
Vista: Muestra «3 unidades · 36,00 €» o el mensaje correspondiente.
Resultado: Tres unidades son aceptadas; seis se rechazan sin cambiar el carrito. Los 36 € son subtotal, sin portes ni impuestos adicionales en este caso.
Qué cuidar: El límite no debe vivir solo en el botón o en JavaScript del navegador: también se aplica al ejecutar la operación de negocio.
EJEMPLO 04 · Cambio de estado y rechazo
Reservar plazas de un taller
Acción: Quedan tres plazas. Llega una solicitud de dos y después otra de dos.
Modelo: Comprueba que la cantidad sea positiva y que haya plazas; confirma la primera reserva y conserva una plaza libre.
Controlador: Traduce la entrada, solicita la reserva y transforma el resultado en una respuesta para el usuario.
Vista: Después de la primera operación muestra la confirmación; después de la segunda, «No hay plazas suficientes».
Resultado: La primera reserva reduce 3 a 1. La segunda se rechaza y el cupo permanece en 1. El ejemplo en Python desarrolla este recorrido.
Qué cuidar: Este cálculo secuencial no resuelve dos solicitudes simultáneas. Un sistema real necesita una operación atómica o un control de concurrencia en el almacenamiento.
EJEMPLO 05 · Reglas que consultan varias entidades
Prestar un ejemplar de biblioteca
Acción: Un lector solicita el ejemplar B17; tiene dos préstamos activos y el máximo del caso es tres.
Modelo: El dominio de préstamos comprueba lector habilitado, ejemplar disponible y límite; registra el préstamo y cambia la disponibilidad.
Controlador: Recibe los identificadores y llama al servicio de préstamo con la identidad del usuario obtenida del contexto de la aplicación.
Vista: Presenta la confirmación y la fecha de devolución proporcionada por el dominio, o explica el rechazo.
Resultado: Si se cumplen las condiciones, el lector pasa de dos a tres préstamos. Un intento adicional falla por el límite; no debe dejar el ejemplar marcado como prestado.
Qué cuidar: Guardar el préstamo y actualizar el ejemplar deben mantener la consistencia. MVC por sí solo no proporciona transacciones.
EJEMPLO 06 · Autorización y validación
Editar un borrador de artículo
Acción: La autora intenta guardar un título no vacío en su borrador.
Modelo: La operación de edición comprueba la regla de título y el permiso sobre ese artículo, y persiste el cambio si procede.
Controlador: Toma la identidad autenticada del contexto, recibe el título y solicita la edición; no confía en un supuesto autor enviado por el formulario.
Vista: Muestra el formulario con el título y los errores, o la confirmación del guardado.
Resultado: Un título válido y un permiso suficiente permiten editar. Un título vacío o un usuario sin permiso no cambian el artículo.
Qué cuidar: Ocultar el botón no impide una solicitud directa. Los permisos se verifican en el servidor; pueden intervenir políticas, filtros y servicios además del controlador.
EJEMPLO 07 · Regla reutilizada en varias interfaces
Aplicar un cupón de descuento
Acción: Una compra elegible de 80 € utiliza un cupón del 10 %.
Modelo: El dominio verifica vigencia y condiciones del cupón, calcula 8 € de descuento y un importe de 72 € antes de otras partidas.
Controlador: Solicita aplicar el cupón y entrega el resultado a la interfaz que está atendiendo.
Vista: La página del carrito y el resumen imprimible muestran el mismo importe, cada uno con su diseño.
Resultado: Cambiar el diseño del resumen no debe cambiar el descuento. Una regla común evita que cada pantalla calcule un total distinto.
Qué cuidar: Las reglas de redondeo y la moneda deben ser explícitas en un sistema monetario. El porcentaje no se recalcula dentro de cada plantilla.
EJEMPLO 08 · MVC de interfaz interactiva
Mostrar un contador en dos vistas
Acción: El usuario pulsa «Sumar» en una aplicación de escritorio que empieza en 4.
Modelo: Conserva el valor y ejecuta el incremento a 5.
Controlador: Interpreta la pulsación y ordena incrementar el contador.
Vista: Una vista textual muestra 5 y una vista gráfica dibuja cinco marcas a partir del mismo estado.
Resultado: Ambas representaciones coinciden. El mecanismo de actualización puede usar notificaciones o una actualización explícita según el entorno.
Qué cuidar: MVC no exige HTTP, SQL ni una base de datos. Las relaciones exactas entre vista y controlador varían entre MVC clásico y frameworks web.
EJEMPLO 09 · Preparación de datos y formato
Presentar un informe mensual
Acción: Se consulta un mes con importes de 100 €, 50 € y una devolución de −20 €.
Modelo: La consulta de negocio selecciona el periodo y calcula un neto de 130 € con las mismas reglas para todas las salidas.
Controlador: Valida el periodo solicitado, llama a la consulta y prepara los datos para el informe.
Vista: Una plantilla HTML muestra «130,00 €»; una salida CSV serializa el valor con el formato acordado.
Resultado: El valor económico es el mismo aunque cambie su representación. En una API, el framework puede devolver datos mediante un serializador sin renderizar una plantilla HTML.
Qué cuidar: No toda respuesta JSON tiene una vista como clase explícita. Conviene describir el flujo concreto del framework y no imponer tres archivos a cualquier respuesta.
EJEMPLO 10 · Separación y pruebas
Cambiar el almacenamiento de una agenda
Acción: Una agenda pasa de guardar contactos en memoria a utilizar un repositorio de base de datos.
Modelo: El dominio conserva las reglas, como exigir un nombre, y usa una abstracción de repositorio para guardar y recuperar contactos.
Controlador: Sigue pidiendo «crear contacto» con los mismos datos de entrada, si el contrato de esa operación se mantiene.
Vista: Conserva su formulario y su confirmación; no necesita conocer la tabla de almacenamiento.
Resultado: Se pueden probar reglas con un repositorio en memoria y comprobar la base real en pruebas de integración.
Qué cuidar: El cambio solo queda aislado si las dependencias están bien separadas. Poner archivos en carpetas Model, View y Controller no garantiza ese resultado.
Qué hace cada componente
Modelo
Representa el problema: plazas, reservas y reglas para aceptarlas. Puede trabajar en memoria o utilizar persistencia.
Vista
Presenta el resultado: nombres, importes, formularios y mensajes. Sus decisiones se refieren al formato de la interfaz.
Controlador
Interpreta una acción y coordina las operaciones y la respuesta. Delega las reglas del dominio para no duplicarlas en cada interfaz.
Por ejemplo, convertir «2» a un entero prepara una entrada; decidir si dos plazas caben en el taller aplica una regla de negocio. La ubicación concreta de validaciones, permisos y servicios depende de la arquitectura, pero no conviene concentrarlo todo en un controlador. [1][4]
El trabajo de Trygve Reenskaug sobre MVC se remonta a 1979 y al entorno de Smalltalk. El MVC clásico y sus adaptaciones web no tienen idénticos mecanismos de comunicación; comparten la intención de separar responsabilidades. [2][3]
Caso resuelto: reservar plazas en Python
Este programa completo utiliza la consola para mostrar las tres responsabilidades. No inicia un servidor ni usa un framework web. Se ejecutó con Python 3.12.14; solo necesita funciones del lenguaje.
Entrada: el controlador recibe el texto «2» y lo convierte a un entero.
Operación: el modelo comprueba el cupo y descuenta dos plazas de las tres iniciales.
Respuesta: la vista recibe mensaje y cupo, y produce el texto de confirmación.
Error: una nueva petición de dos plazas se rechaza; el controlador prepara el mensaje y la vista muestra que sigue quedando una.
Ver y copiar el programa completo
Guárdalo como reservas_mvc.py y ejecútalo con python reservas_mvc.py. La entrada del controlador es texto; sus llamadas de demostración se encuentran al final.
"""Ejemplo didáctico de responsabilidades MVC, sin servidor web."""
class Cupo:
def __init__(self, plazas):
if type(plazas) is not int or plazas < 0:
raise ValueError("El cupo debe ser un entero no negativo")
self._plazas = plazas
@property
def plazas(self):
return self._plazas
def reservar(self, cantidad):
if type(cantidad) is not int or cantidad <= 0:
raise ValueError("La cantidad debe ser positiva")
if cantidad > self._plazas:
raise ValueError("No hay plazas suficientes")
self._plazas -= cantidad
def vista(datos):
return f"{datos['mensaje']} | Plazas libres: {datos['plazas']}"
def controlador(modelo, entrada):
try:
cantidad = int(entrada)
except ValueError:
mensaje = "Escribe un número entero"
else:
try:
modelo.reservar(cantidad)
except ValueError as error:
mensaje = str(error)
else:
mensaje = "Reserva confirmada"
datos = {"mensaje": mensaje, "plazas": modelo.plazas}
return vista(datos)
if __name__ == "__main__":
cupo = Cupo(3)
for texto in ["2", "2", "dos", "0"]:
print(controlador(cupo, texto))
Reserva confirmada | Plazas libres: 1
No hay plazas suficientes | Plazas libres: 1
Escribe un número entero | Plazas libres: 1
La cantidad debe ser positiva | Plazas libres: 1
Cupo.reservar valida antes de modificar el estado. La función vista solo construye texto; controlador no calcula por su cuenta cuántas plazas deben quedar. Una entrada no numérica, una cantidad cero o una reserva excesiva mantienen el cupo.
Es una simulación secuencial en memoria. No conserva las reservas al cerrar el programa ni cubre usuarios, pagos, persistencia o concurrencia. Esas responsabilidades requieren componentes y pruebas adicionales en una aplicación real.
MVC no equivale a una arquitectura de tres capas
MVC organiza la interacción y la presentación. Las capas de presentación, negocio y acceso a datos organizan dependencias más amplias; los niveles de despliegue indican dónde se ejecutan los componentes. Una aplicación puede combinar estas decisiones. [4]
Tres preguntas distintas de diseño
Decisión
Pregunta
Ejemplo
MVC
¿Quién interpreta, opera y presenta?
Controlador de reservas, dominio del cupo y vista de confirmación.
Capas lógicas
¿Cómo se separan las dependencias?
Interfaz, servicio de reservas y repositorio de almacenamiento.
Despliegue
¿En qué procesos o equipos se ejecuta?
Aplicación y base de datos en procesos distintos, o en una misma máquina.
No hay una correspondencia obligatoria «modelo = base de datos, controlador = negocio, vista = servidor de presentación». Un modelo de dominio puede existir sin base de datos, y una aplicación MVC puede utilizar servicios externos.
Qué facilita y qué no garantiza
La separación ayuda a probar reglas sin abrir pantallas y a cambiar formatos sin reescribir esas reglas. Para lograrlo hay que controlar las dependencias. También hacen falta pruebas de integración entre componentes y pruebas del recorrido completo: una prueba unitaria correcta no garantiza que el formulario envíe el campo adecuado.
Frameworks y enfoques relacionados con MVC
El patrón puede implementarse sin framework. Estas tecnologías muestran distintas formas de organizar aplicaciones; sus nombres no son instrucciones de Java, PHP o Perl, y no todas utilizan el mismo flujo de acciones.
ASP.NET MVC y ASP.NET Core MVC
ASP.NET MVC 4 es una versión histórica del framework MVC; su número no significa simplemente «ASP.NET 4.0». ASP.NET Core MVC es una línea distinta de la plataforma .NET que implementa este patrón para aplicaciones web. Sus controladores coordinan peticiones y sus vistas pueden usar Razor. [1][13]
Spring Web MVC
El módulo spring-webmvc organiza aplicaciones web sobre la API Servlet. Su infraestructura despacha solicitudes hacia los componentes que las atienden. Spring WebFlux utiliza otra pila; no son dos nombres del mismo módulo. [5]
Apache Struts
Framework para aplicaciones web Java con arquitectura MVC. Su infraestructura de solicitudes y resultados ayuda a separar navegación, presentación y lógica de la aplicación. Adoptarlo no traslada automáticamente las reglas de negocio al lugar correcto. [6]
KumbiaPHP
Framework de PHP que se presenta como MVC. Es un ejemplo de estructura aportada por una biblioteca de desarrollo; MVC no es una instrucción especial del lenguaje PHP. [7]
JavaServer Faces / Jakarta Faces
La tecnología Faces sigue un enfoque de componentes de interfaz en el servidor, con estado, eventos, conversiones y validación. Para comprender su funcionamiento hay que estudiar ese ciclo de vida, no copiar literalmente el flujo de un controlador de acciones de otro framework. [8]
PRADO
Framework PHP basado en componentes y eventos. Permite organizar comportamiento y presentación, pero describirlo únicamente como un framework MVC de acciones ocultaría su modelo de programación. [9]
AuroraMVC: referencia histórica
La ficha del proyecto Java AuroraMVC lo describe como un framework MVC diseñado para trabajar con Spring. Sirve para reconocer una implementación histórica; esa ficha no acredita compatibilidad con una versión actual de Spring ni que cualquier producto llamado Aurora sea el mismo proyecto. [10]
Apache::PageKit: referencia histórica
La documentación de mod_perl de 2014 describe PageKit con separación de modelo, vista, controlador y contenido. Es un antecedente de organización web en Perl; no se presenta aquí como recomendación de instalación actual. [11]
CGI::Builder: contraste histórico
Su documentación de la versión 1.36 describe un proceso por fases y manejadores para construir aplicaciones web en Perl. Esa organización no basta, por sí sola, para afirmar que todas las aplicaciones creadas con él siguen MVC. [12]
8 ejercicios para practicar
Una plantilla ejecuta SQL y decide si un préstamo está permitido. ¿Qué separarías?
Un usuario solicita seis artículos cuando el máximo es cinco. ¿Basta con desactivar el botón?
Quedan tres plazas y se reservan dos. Otra persona pide dos más. ¿Cuál es el estado final del caso secuencial?
¿Son MVC y tres servidores equivalentes?
La entrada de plazas es «dos». ¿Se ha incumplido el cupo?
Un informe HTML muestra 130 € y el CSV muestra 150 €. ¿Es una diferencia de formato?
Un contador mantiene sus datos solo en memoria. ¿Puede organizarse con MVC?
¿Qué prueba harías para comprobar que una reserva rechazada no descuenta plazas?
Ver respuestas razonadas
La consulta y la regla de préstamo deben pasar al dominio o a servicios/repositorios. La vista presenta los datos y el resultado; el controlador coordina la solicitud.
No. La operación de negocio debe rechazar la cantidad también al recibir una solicitud directa; el carrito no debe cambiar.
Queda una plaza; la segunda operación se rechaza sin modificar el estado.
No. MVC separa responsabilidades de interacción y presentación; el despliegue en procesos o servidores es otra decisión.
Todavía no se ha podido interpretar un entero. Es un error de entrada; no se llama a la reserva con una cantidad válida.
No: los importes difieren. Hay que revisar filtros y reglas compartidas, incluida la devolución de 20 €.
Sí. El modelo representa estado y comportamiento; la persistencia en base de datos no es obligatoria.
Preparar un cupo conocido, solicitar una cantidad superior, comprobar el rechazo y comparar el estado anterior con el posterior.
Errores frecuentes al explicar MVC
Modelo reducido a SQL: Incluye estado y comportamiento del dominio; el acceso a datos puede delegarse en un repositorio.
Controlador que hace todo: Coordinar una petición no exige duplicar cálculos, permisos y persistencia en cada acción.
Reglas dentro de la plantilla: Una vista puede elegir formato; no debe convertirse en la única autoridad sobre cupos o descuentos.
Validación solo en el navegador: La operación del servidor también comprueba entradas y reglas; los controles visuales se pueden omitir.
Tres carpetas como prueba de buen diseño: Importan las responsabilidades y dependencias reales, no solo los nombres de archivos.
Confundir MVC con tres máquinas: El patrón no fija la cantidad de procesos, bases de datos o servidores.
Suponer que MVC elimina los conflictos: Concurrencia, permisos y consistencia necesitan soluciones propias.
Presentar todos los frameworks como iguales: Una infraestructura de acciones, un ciclo de componentes y un sistema de manejadores pueden distribuir las tareas de modo distinto.