# Model-View-Controller (MVC) Pattern

## Problem

User interface code, business logic, and data representation become entangled in a single monolith. Changes to the UI break business rules, changes to data structures break the display, and the entire system resists testing and reuse.

## Solution

**MVC** separates an application into three interconnected components:

- **Model** — Manages the data, business rules, and state. Notifies observers of state changes.
- **View** — Renders the model to the user. Reads from the model but does not modify it directly.
- **Controller** — Receives user input, translates it into commands for the model or view.

The controller updates the model, the model notifies the view, and the view re-renders.

## When to Use

- Web applications with clear request/response cycles.
- GUI applications where multiple views display the same data.
- Applications where business logic must be tested independently of UI.
- Teams where different developers work on UI vs. business logic.

## When to Avoid

- Very simple apps where the separation adds unnecessary boilerplate.
- Real-time reactive UIs where MVVM or unidirectional data flow is a better fit.
- When the framework already imposes a different paradigm (e.g., component-based).

## Pseudocode

```
CLASS Model:
    data = initial_state
    observers = []

    METHOD update(new_data):
        data = new_data
        NOTIFY all observers

CLASS View:
    METHOD render(model):
        DISPLAY model.data

CLASS Controller:
    MODEL model
    VIEW view

    METHOD handle_input(action, payload):
        IF action == "update":
            model.update(payload)
        view.render(model)
```

## Python

```python
class TaskModel:
    def __init__(self) -> None:
        self.tasks: list[dict[str, object]] = []
        self._observers: list = []

    def add_observer(self, observer) -> None:
        self._observers.append(observer)

    def _notify(self) -> None:
        for obs in self._observers:
            obs.on_model_changed(self)

    def add_task(self, title: str) -> None:
        self.tasks.append({"title": title, "done": False})
        self._notify()

    def complete_task(self, index: int) -> None:
        if 0 <= index < len(self.tasks):
            self.tasks[index]["done"] = True
            self._notify()


class TaskView:
    def on_model_changed(self, model: TaskModel) -> None:
        self.render(model)

    def render(self, model: TaskModel) -> None:
        print("  [View] Current tasks:")
        if not model.tasks:
            print("    (no tasks)")
        for i, task in enumerate(model.tasks):
            status = "done" if task["done"] else "pending"
            print(f"    [{i}] {task['title']} ({status})")
        print()


class TaskController:
    def __init__(self, model: TaskModel, view: TaskView) -> None:
        self.model = model
        self.view = view
        self.model.add_observer(self.view)

    def add_task(self, title: str) -> None:
        print(f"  [Controller] Adding task: {title}")
        self.model.add_task(title)

    def complete_task(self, index: int) -> None:
        print(f"  [Controller] Completing task at index {index}")
        self.model.complete_task(index)


def main() -> None:
    model = TaskModel()
    view = TaskView()
    controller = TaskController(model, view)

    print("--- Add tasks ---")
    controller.add_task("Write documentation")
    controller.add_task("Fix bug #42")
    controller.add_task("Review PR")

    print("--- Complete task 1 ---")
    controller.complete_task(1)

    print("--- Complete task 0 ---")
    controller.complete_task(0)


if __name__ == "__main__":
    main()
```

**Output:**
```
--- Add tasks ---
  [Controller] Adding task: Write documentation
  [View] Current tasks:
    [0] Write documentation (pending)

  [Controller] Adding task: Fix bug #42
  [View] Current tasks:
    [0] Write documentation (pending)
    [1] Fix bug #42 (pending)

  [Controller] Adding task: Review PR
  [View] Current tasks:
    [0] Write documentation (pending)
    [1] Fix bug #42 (pending)
    [2] Review PR (pending)

--- Complete task 1 ---
  [Controller] Completing task at index 1
  [View] Current tasks:
    [0] Write documentation (pending)
    [1] Fix bug #42 (done)
    [2] Review PR (pending)

--- Complete task 0 ---
  [Controller] Completing task at index 0
  [View] Current tasks:
    [0] Write documentation (done)
    [1] Fix bug #42 (done)
    [2] Review PR (pending)
```

## JavaScript

```javascript
class TaskModel {
  constructor() {
    this.tasks = [];
    this._observers = [];
  }

  addObserver(observer) {
    this._observers.push(observer);
  }

  _notify() {
    for (const obs of this._observers) {
      obs.onModelChanged(this);
    }
  }

  addTask(title) {
    this.tasks.push({ title, done: false });
    this._notify();
  }

  completeTask(index) {
    if (index >= 0 && index < this.tasks.length) {
      this.tasks[index].done = true;
      this._notify();
    }
  }
}

class TaskView {
  onModelChanged(model) {
    this.render(model);
  }

  render(model) {
    console.log("  [View] Current tasks:");
    if (model.tasks.length === 0) {
      console.log("    (no tasks)");
    }
    model.tasks.forEach((task, i) => {
      const status = task.done ? "done" : "pending";
      console.log(`    [${i}] ${task.title} (${status})`);
    });
    console.log();
  }
}

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

  addTask(title) {
    console.log(`  [Controller] Adding task: ${title}`);
    this.model.addTask(title);
  }

  completeTask(index) {
    console.log(`  [Controller] Completing task at index ${index}`);
    this.model.completeTask(index);
  }
}

// --- Main ---
const model = new TaskModel();
const view = new TaskView();
const controller = new TaskController(model, view);

console.log("--- Add tasks ---");
controller.addTask("Write documentation");
controller.addTask("Fix bug #42");
controller.addTask("Review PR");

console.log("--- Complete task 1 ---");
controller.completeTask(1);

console.log("--- Complete task 0 ---");
controller.completeTask(0);
```

**Output:**
```
--- Add tasks ---
  [Controller] Adding task: Write documentation
  [View] Current tasks:
    [0] Write documentation (pending)

  [Controller] Adding task: Fix bug #42
  [View] Current tasks:
    [0] Write documentation (pending)
    [1] Fix bug #42 (pending)

  [Controller] Adding task: Review PR
  [View] Current tasks:
    [0] Write documentation (pending)
    [1] Fix bug #42 (pending)
    [2] Review PR (pending)

--- Complete task 1 ---
  [Controller] Completing task at index 1
  [View] Current tasks:
    [0] Write documentation (pending)
    [1] Fix bug #42 (done)
    [2] Review PR (pending)

--- Complete task 0 ---
  [Controller] Completing task at index 0
  [View] Current tasks:
    [0] Write documentation (done)
    [1] Fix bug #42 (done)
    [2] Review PR (pending)
```

## Related Patterns

- **MVVM** — MVVM replaces the controller with a ViewModel and uses data binding; better suited to rich desktop/SPA UIs.
- **MVP** — MVP makes the view passive and gives the presenter full control; better for testability in the absence of data binding.
- **Observer** — the model notifies views of state changes using the observer pattern.
- **Repository** — controllers typically delegate data access to repositories rather than touching the database directly.
