MVC — Model-View-Controller

Introduktion

MVC är ett av de mest använda arkitekturmönstren inom webbutveckling. Det delar upp en applikation i tre sammankopplade delar med tydliga ansvarsområden — vilket gör koden lättare att underhålla, testa och vidareutveckla.


De tre delarna

Model — Datalagret

Modellen representerar applikationens data och affärslogik. Den är helt oberoende av hur data presenteras.

Ansvar:

  • Hämta och spara data (databas, API)
  • Validera indata
  • Tillämpa affärsregler
  • Notifiera View om förändringar (via Observer i klassisk MVC)

View — Presentationslagret

View ansvarar för att presentera data för användaren. Den innehåller ingen affärslogik — bara renderingslogik.

Ansvar:

  • Rendera HTML/UI baserat på data från Modellen
  • Ta emot och vidarebefordra användarinteraktioner till Controller
  • Uppdatera sig när Modellen ändras

Controller — Koordinatorn

Controller är limmet som håller ihop Model och View. Den tar emot användarens input, bearbetar den och avgör vad som ska hända.

Ansvar:

  • Ta emot HTTP-förfrågningar
  • Validera och tolka indata
  • Anropa rätt metoder på Modellen
  • Bestämma vilken View som ska renderas

Dataflöde

Användare → View → Controller → Model → Controller → View → Användare
  1. Användaren klickar på “Visa alla produkter”
  2. Controllern tar emot HTTP GET /products
  3. Controllern anropar Product::getAll() på Modellen
  4. Modellen hämtar data från databasen och returnerar den
  5. Controllern skickar data till View products/index.html
  6. View renderar HTML med produktlistan
  7. Svaret skickas tillbaka till användaren

Exempel — Produktsystem (PHP / Laravel-stil)

<?php

// ── Model ─────────────────────────────────────────────────

class Product
{
    public function __construct(
        public readonly int    $id,
        public readonly string $name,
        public readonly float  $price
    ) {}

    // Hämtar alla produkter från array "databasen"
    public static function getAll(): array
    {
    	
        return [ // Hårdkodad data för exemplet
            new self(1, 'Laptop Pro',    14999.00),
            new self(2, 'Trådlös Mus',     399.00),
            new self(3, 'Mekaniskt tangentbord', 1299.00),
        ];
    }

    public static function findById(int $id): ?self
    {
        foreach (self::getAll() as $product) {
            if ($product->id === $id) return $product;
        }
        return null;
    }
}

// ── View (template) ───────────────────────────────────────

class ProductView
{
    public function renderList(array $products): string
    {
        $rows = array_map(fn($p) =>
            "<tr><td>{$p->id}</td><td>{$p->name}</td><td>{$p->price} kr</td></tr>",
            $products
        );

        return 
        "<table><thead><tr><th>ID</th><th>Namn</th><th>Pris</th></tr></thead>"
             . "<tbody>" . implode('', $rows) . "</tbody></table>";
    }

    public function renderDetail(Product $product): string
    {
        return "<h1>{$product->name}</h1><p>Pris: {$product->price} kr</p>";
    }

    public function renderNotFound(): string
    {
        return "<p>Produkten hittades inte.</p>";
    }
}

// ── Controller ────────────────────────────────────────────

class ProductController
{
    public function __construct(private ProductView $view) {}

    // GET /products
    public function index(): string
    {
        $products = Product::getAll();
        return $this->view->renderList($products);
    }

    // GET /products/{id}
    public function show(int $id): string
    {
        $product = Product::findById($id);

        if ($product === null) {
            http_response_code(404);
            return $this->view->renderNotFound();
        }

        return $this->view->renderDetail($product);
    }
}

// ── Router / Bootstrap ────────────────────────────────────

$controller = new ProductController(new ProductView());

// Simulera en HTTP-förfrågan
$uri = $_SERVER['REQUEST_URI'] ?? '/products';

if ($uri === '/products') {
    echo $controller->index();
} elseif (preg_match('#^/products/(\d+)$#', $uri, $m)) {
    echo $controller->show((int) $m[1]);
}

MVC i moderna ramverk

RamverkSpråkModelViewController
LaravelPHPEloquent ORMBlade-templatesHttp/Controllers/
Spring MVCJavaJPA / HibernateThymeleaf / JSP@Controller
ASP.NET MVCC#Entity FrameworkRazor PagesController
Ruby on RailsRubyActive RecordERBApplicationController

Fördelar

  • Separation of Concerns — Varje del har ett tydligt ansvar.
  • Testbarhet — Modellen kan testas utan UI; Controller utan databas.
  • Parallell utveckling — Frontend och backend kan arbeta oberoende.
  • Återanvändbarhet — Samma Model kan visas av flera Views (web, cli osv).

Nackdelar

  • Kan vara overkill för enkla applikationer med lite logik.
  • Controller kan bli överfull (Fat Controller) om man inte är disciplinerad.
  • Kan leda till tätt kopplad View-Controller i klassisk implementering.

Av Victor Hernandez från Bytebase.se