MVC Pattern
debt(d7/e5/b7/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints note automated=no, and while tools like phpmd, deptrac, and phpstan are listed, they can flag proxy signals (controller method >50 lines, DB query in controller) but cannot reliably detect the full spectrum of layer violations — especially anemic models, business logic misplacement, or views with logic that belongs in a presenter. These require human judgment in code review.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix describes keeping controllers thin and moving logic to services/use cases, but fat controllers and fat models are typically pervasive — extraction into service layers touches multiple controllers, models, and introduces new files (services, use cases, presenters). This is more than a single-line swap but usually contained within the application layer rather than a full architectural rework.
Closest to 'strong gravitational pull' (b7). MVC is an architectural pattern that applies across all web/api contexts (per applies_to). Every controller, model, and view written is shaped by the team's understanding of MVC layer responsibilities. Misapplied MVC (fat controllers, anemic models) creates a persistent structural gravity — every new feature added to the codebase either reinforces or fights the existing layer misuse, shaping all future development decisions.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly states that developers wrongly believe MVC strictly defines where all application logic must live, leading to fat controllers and anemic models. This is a well-documented, common misunderstanding that most developers encounter and eventually correct, but it is not as catastrophic as contradicting an entirely different paradigm — it maps to t5.
Also Known As
TL;DR
Explanation
MVC divides an application into three layers: the Model represents business logic and data; the View renders the UI from model data; the Controller handles HTTP requests, coordinates models, and selects views. This separation makes each layer independently testable and replaceable. PHP frameworks (Laravel, Symfony, CodeIgniter) implement MVC with routing, ORM, and templating engines. In practice, Controllers are often overloaded — move business logic into dedicated Service or Domain classes, leaving Controllers thin orchestrators.
Common Misconception
Why It Matters
Common Mistakes
- Fat controllers with business logic — controllers should only coordinate, not compute.
- Fat models that are aware of HTTP, session, or rendering — models should be pure domain logic.
- Views with PHP loops and conditionals that belong in a presenter or view model.
- Treating MVC as a complete architecture — it describes layers, not where to put all business logic.
Code Examples
// Fat controller — business logic in the wrong layer:
class OrderController {
public function place(Request $req): Response {
$total = 0;
foreach ($req->items as $item) { // Business logic
$total += $item['price'] * $item['qty'] * (1 - ($item['discount'] ?? 0));
}
if ($total < 0) throw new InvalidOrderException();
// Should delegate to OrderService — controller only orchestrates
}
}
// Model — domain data and business rules
class Order extends Model {
public function isPaid(): bool { return $this->status === 'paid'; }
public function total(): Money { return Money::of($this->total_cents, 'GBP'); }
}
// View — presentation only (Blade template)
// resources/views/orders/show.blade.php
// <h1>Order #{{ $order->id }}</h1>
// <p>Total: {{ $order->total() }}</p>
// Controller — thin glue: validate input, call service, return view
class OrderController extends Controller {
public function show(int $id): View {
$order = Order::findOrFail($id);
$this->authorize('view', $order);
return view('orders.show', compact('order'));
}
}
// Business logic lives in services/domain, not the controller