StackPractices
intermediate Por Mathias Paulenko

Patrón MVC

Separa la aplicación en componentes Modelo, Vista y Controlador. Patrón de diseño arquitectural para código organizado y mantenible.

Visión general

El Patrón Modelo-Vista-Controlador (MVC) es un patrón de diseño arquitectural que separa una aplicación en tres componentes interconectados: Modelo (datos y lógica de negocio), Vista (presentación) y Controlador (manejo de entrada y coordinación).

Es la base de muchos frameworks web (Django, Ruby on Rails, ASP.NET MVC) y arquitecturas de aplicaciones de escritorio.

Cuándo usarlo

Usa el Patrón MVC cuando:

  • Quieres una separación limpia entre datos, UI y lógica de interacción de usuario
  • Múltiples vistas necesitan mostrar los mismos datos (ej. web y móvil)
  • La UI cambia frecuentemente pero el modelo de datos subyacente permanece estable
  • Necesitas soportar diferentes mecanismos de entrada (web, CLI, API)
  • Múltiples desarrolladores trabajan en diferentes capas simultáneamente

Solución

Python

class UserModel:
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email

class UserView:
    def display(self, user: UserModel):
        print(f"User: {user.name} ({user.email})")

class UserController:
    def __init__(self, model: UserModel, view: UserView):
        self.model = model
        self.view = view

    def update_name(self, name: str):
        self.model.name = name
        self.view.display(self.model)

# Uso
controller = UserController(UserModel("Alice", "alice@example.com"), UserView())
controller.update_name("Alicia")

JavaScript

class UserModel {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

class UserView {
  display(user) {
    console.log(`User: ${user.name} (${user.email})`);
  }
}

class UserController {
  constructor(model, view) {
    this.model = model;
    this.view = view;
  }

  updateName(name) {
    this.model.name = name;
    this.view.display(this.model);
  }
}

// Uso
const controller = new UserController(
  new UserModel("Alice", "alice@example.com"),
  new UserView()
);
controller.updateName("Alicia");

Java

class UserModel {
    String name;
    String email;
    UserModel(String name, String email) {
        this.name = name;
        this.email = email;
    }
}

class UserView {
    void display(UserModel user) {
        System.out.println("User: " + user.name + " (" + user.email + ")");
    }
}

class UserController {
    private UserModel model;
    private UserView view;
    UserController(UserModel model, UserView view) {
        this.model = model;
        this.view = view;
    }
    void updateName(String name) {
        model.name = name;
        view.display(model);
    }
}

// Uso
UserController controller = new UserController(
    new UserModel("Alice", "alice@example.com"),
    new UserView()
);
controller.updateName("Alicia");

Explicación

MVC divide la responsabilidad en tres capas:

  • Modelo: Gestiona datos y reglas de negocio. Notifica a las vistas cuando los datos cambian.
  • Vista: Renderiza los datos del modelo. En aplicaciones modernas, esto suele ser una plantilla o componente.
  • Controlador: Acepta entrada de usuario, la procesa y actualiza el modelo o la vista según corresponda.

En frameworks web modernos, el Controlador suele mapear rutas HTTP a operaciones del Modelo, mientras que la Vista se renderiza del lado del servidor o como aplicación de página única.

Variantes

VarianteCaso de usoCompromiso
MVC clásicoAplicaciones de escritorio (estilo Smalltalk)Acoplamiento fuerte vista-modelo
MVPCapas de UI testeablesEl Presenter se convierte en “god class”
MVVMFrameworks frontend (Vue, Angular)El binding bidireccional añade complejidad

Lo que funciona

  • Mantén los Modelos ignorantes de las Vistas: Los modelos no deben saber cómo se muestran
  • Haz las Vistas de solo lectura desde el Modelo: Las vistas observan modelos, pero no los modifican directamente
  • Mantén los Controladores delgados: La lógica de negocio pertenece al Modelo, no al Controlador
  • Usa el Patrón Observer para actualizaciones Modelo-a-Vista y reducir acoplamiento
  • Evita acceso directo al Modelo desde Vistas: Siempre pasa por el Controlador o un ViewModel

Errores comunes

  • Controladores gordos: Poner lógica de negocio en controladores en lugar de modelos
  • Modelos guiados por vistas: Cambiar la estructura del modelo para satisfacer las necesidades de una vista específica
  • Acoplamiento fuerte: Las vistas llamando directamente a métodos del modelo en lugar de usar eventos
  • Sobre-ingeniería: Usar MVC completo para un script simple donde la separación no aporta valor
  • Ignorar el flujo de datos: Permitir que las vistas modifiquen modelos directamente, saltándose el controlador

Lectura Adicional

  • Documentación oficial: consulta la referencia actualizada del framework o herramienta utilizada.
  • Guías relacionadas: explora las guías de architectural y architecture para profundizar.
  • Patrones complementarios: revisa los patrones de diseño aplicables a tu stack tecnológico.
  • Postmortems públicos: estudia incidentes reales de equipos que enfrentaron problemas similares en producción.

Notas de Producción

  • Despliega gradualmente usando canary o blue-green para detectar regresiones temprano.
  • Configura alertas para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
  • Documenta el rollback en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
  • Revisa logs estructurados con correlation IDs para trazar requests end-to-end en incidentes.

Puntos Clave

  • Aplica patrón mvc cuando necesites una solución práctica para tu caso de uso.
  • Monitorea el rendimiento después de implementar; mide latencia, errores y uso de recursos antes y después.
  • Revisa la sección de Troubleshooting ante errores comunes; la mayoría tienen causa raíz documentada con solución.
  • Mantén dependencias actualizadas y ejecuta tests en CI para prevenir regresiones en producción.

Temas Avanzados

Escenario: MVC para Dashboard Web

// MVC pattern: Model, View, Controller
// Model: datos y logica de negocio
class DashboardModel {
  private metrics: Metric[] = [];
  private subscribers: (() => void)[] = [];

  addMetric(metric: Metric) {
    this.metrics.push(metric);
    this.notify();
  }

  getMetrics(): Metric[] { return [...this.metrics]; }

  subscribe(cb: () => void) { this.subscribers.push(cb); }
  private notify() { this.subscribers.forEach(cb => cb()); }
}

// View: renderiza la UI
class DashboardView {
  constructor(private container: HTMLElement) {}

  render(metrics: Metric[]) {
    this.container.innerHTML = metrics.map(m =>
      `<div class="qm">`,
      `<h3>${m.name}</h3>`,
      `<span class="value">${m.value}</span>`,
      `</div>`
    ).join("");
  }
}

// Controller: orquesta Model y View
class DashboardController {
  constructor(private model: DashboardModel, private view: DashboardView) {
    this.model.subscribe(() => this.updateView());
  }

  async loadMetrics() {
    const data = await fetch("/api/metrics").then(r => r.json());
    data.forEach((m: Metric) => this.model.addMetric(m));
  }

  private updateView() {
    this.view.render(this.model.getMetrics());
  }
}

// Uso
const model = new DashboardModel();
const view = new DashboardView(document.getElementById("dashboard")!);
const controller = new DashboardController(model, view);
controller.loadMetrics();

// Comparacion MVC vs MVP vs MVVM
  | Patron | View conoce Model? | Acoplamiento | Data binding |
  |--------|-------------------|-------------|--------------|
  | MVC | Si (directamente) | Alto | Manual |
  | MVP | No (via Presenter) | Medio | Manual |
  | MVVM | No (via ViewModel) | Bajo | Automatico (Observable) |

Lecciones:

  • MVC separa datos (Model), UI (View) y logica (Controller)
  • El Controller es el unico que toca Model y View
  • Model notifica cambios a traves de observers
  • MVC es simple pero View puede acoplarse al Model
  • Para apps modernas, MVVM con data binding es preferible

### MVC vs MVVM: cual uso en frontend?

Usa MVC para apps simples donde la View es estatica y el Controller maneja todo. Usa MVVM (Model-View-ViewModel) para apps con data binding: la View se actualiza automaticamente cuando el ViewModel cambia. React usa un patron similar a MVVM (state + JSX). Angular usa MVVM con Change Detection. Vue usa MVVM con reactivity. MVC es mas comun en backend (Spring MVC, Express MVC).








End of document. Review and update quarterly.

## Troubleshooting

- **High latency between services**: trace the request path.   Look for synchronous chains, missing caching, and oversized payloads that cross network boundaries.
- **Single point of failure**: identify components without redundancy.   Add replicas, failover, or circuit breakers before scaling traffic.
- **Unexpected coupling between services**: review shared databases, libraries, and schemas.   Bound contexts should own their data and expose stable interfaces.
- **Cost spikes after scaling**: Reserved capacity or spot instances can reduce steady-state spend.
- **Difficult to reason about the system**: maintain architecture decision records and service dependency maps.

## Errores Comunes en Producción

- Aplicar el patrón donde no se necesita abstracción, agregando complejidad accidental.
- Dejar que el patrón se filtre en módulos no relacionados y confundir los límites de responsabilidad.
- Sobre-ingeniería en la primera implementación en lugar de comenzar simple y medir el dolor.
- Saltar los tests de contrato, de modo que las refactorizaciones rompan consumidores en silencio.
- Ignorar modos de fallo que el patrón no cubre.
- Usar el patrón como opción por defecto en lugar de elegir la herramienta adecuada para la escala actual.
- Olvidar documentar cuándo dejar de usar el patrón y qué lo reemplaza.
- Carecer de observabilidad sobre rendimiento y propagación de errores del patrón.

Preguntas frecuentes

¿Es este patrón adecuado para proyectos pequeños?

Para proyectos pequeños con pocos componentes, este patrón puede añadir complejidad innecesaria. Empieza simple e introduce el patrón cuando sientas el problema que resuelve.

¿Cómo se compara este patrón con alternativas?

Cada patrón hace diferentes trade-offs. Revisa la tabla de variantes arriba y considera tus restricciones específicas: tamaño del equipo, requisitos de rendimiento y planes de escalado.

¿Puedo aplicar este patrón parcialmente?

Sí. Muchos equipos adoptan patrones incrementalmente. Empieza con la idea central y añade sofisticación según sea necesario. El patrón es una guía, no un blueprint estricto.