---
title: Use Service Layer — Controllers Must Not Contain Business Logic
impact: HIGH
impactDescription: fat controllers are untestable, unmaintainable, and couple HTTP concerns with domain logic
tags: service-layer, architecture, clean-code, controller, laravel
---

## Use Service Layer — Controllers Must Not Contain Business Logic

Controllers handle HTTP: parse request, call service, return response. Domain logic (calculations, business rules, orchestration) belongs in a dedicated Service class.

**Wrong:**

```php
// OrderController doing everything — HTTP + business + DB
public function store(StoreOrderRequest $request)
{
    $subtotal = 0;
    foreach ($request->items as $item) {
        $product = Product::find($item['product_id']);
        if ($product->stock < $item['qty']) {
            return response()->json(['error' => 'Insufficient stock'], 422);
        }
        $subtotal += $product->price * $item['qty'];
    }

    $discount = $request->user()->isPremium() ? $subtotal * 0.1 : 0;
    $total = $subtotal - $discount + $this->calculateShipping($request->address);

    $order = Order::create([...]);
    foreach ($request->items as $item) { /* create OrderItem, deduct stock */ }

    Mail::to($request->user())->send(new OrderConfirmation($order));
    return OrderResource::make($order);
}
```

**Correct:**

```php
// Service contains all business logic
class OrderService
{
    public function __construct(
        private readonly InventoryService $inventory,
        private readonly PricingService   $pricing,
    ) {}

    public function placeOrder(User $user, array $items, Address $address): Order
    {
        $this->inventory->reserveItems($items);        // throws on insufficient stock
        $total = $this->pricing->calculate($items, $user, $address);

        return DB::transaction(function () use ($user, $items, $total) {
            $order = Order::create(['user_id' => $user->id, 'total' => $total]);
            $this->inventory->commitReservation($order, $items);
            SendOrderConfirmation::dispatch($order);
            return $order;
        });
    }
}

// Controller is just HTTP glue
class OrderController
{
    public function __construct(private readonly OrderService $orderService) {}

    public function store(StoreOrderRequest $request): JsonResponse
    {
        $order = $this->orderService->placeOrder(
            $request->user(),
            $request->validated('items'),
            Address::fromRequest($request),
        );
        return OrderResource::make($order)->response()->setStatusCode(201);
    }
}
```
