Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти
Інше
Події Про нас

Куди класти бізнес-логіку: Service-класи, Action-класи чи «товсті» моделі?

Одне з ключових архітектурних рішень. Контролери й моделі швидко «розпухають», тож логіку виносять в окремі класи.

Проблема «товстих» контролерів: контролер має лише приймати запит, делегувати роботу й повертати відповідь. Бізнес-логіка в ньому не тестується ізольовано й не перевикористовується.

Service-класи - групують пов'язану логіку домену:

class OrderService
{
    public function __construct(
        private PaymentGateway $gateway,
        private InventoryManager $inventory,
    ) {}

    public function place(User $user, Cart $cart): Order
    {
        return DB::transaction(function () use ($user, $cart) {
            $this->inventory->reserve($cart->items);
            $order = $user->orders()->create([/* ... */]);
            $this->gateway->charge($user, $cart->total);
            OrderPlaced::dispatch($order);

            return $order;
        });
    }
}

Action-класи (single-action) - один клас = одна операція. Дрібніша гранулярність, дуже тестовано:

class PlaceOrderAction
{
    public function handle(User $user, Cart $cart): Order { /* ... */ }
}

«Товсті» моделі - логіку, тісно пов'язану з даними самої моделі (скоупи, аксесори, прості методи стану), доречно лишати в моделі. Складні міждоменні операції - у сервіси.

Рекомендації:

  • Контролер тонкий: запит → виклик сервісу/екшену → відповідь.
  • Складні операції з кількома моделями - у Service/Action із транзакцією.
  • Логіка одного агрегату - у моделі (скоупи, обчислювані атрибути).
  • Не плодіть абстракцій передчасно - починайте простіше, виносьте за потреби.

Докладніше в документації: Service Container

Схожі питання