Завантаження публікації
ОГОЛОШЕННЯ

Service layer у Laravel: чіткі правила, що лишається в контролері, а що йде в сервіс

Практичний стандарт для проєктів з адмінкою: як зробити тонкий контролер, де тримати бізнес-логіку, транзакції, інтеграції та побічні ефекти, щоб код не розповзався і не ламався.


Максим Третяк
Максим Третяк
Газета Дейком | 01.01.2026, 12:00 GMT+3; 05:00 GMT-4

Service layer потрібен не “для краси”, а щоб проєкт не перетворився на набір випадкових контролерів із копіпастом. Контролер — це шар HTTP, він має працювати як диспетчер: прийняти запит, запустити правильний сценарій і повернути відповідь. Сервіс — це шар сценаріїв (use-cases): “створити тег”, “оновити секцію”, “опублікувати новину”, “прикріпити медіа”, “записати активність”. Якщо ти чітко розділиш ці ролі, код стає прогнозованим: будь-яка нова фіча додається по одному шаблону.

Перше правило: у контролері не повинно бути бізнес-рішень. Контролер не вирішує, “чи можна публікувати”, “як рахувати суму”, “як генерувати slug”, “які статуси допустимі”. Він може перевірити доступ (через middleware/policy), але самі правила зміни стану належать сервісу. Якщо правило буде в контролері, завтра ти додаси ще один маршрут або CLI-команду — і ти почнеш дублювати логіку або отримаєш різну поведінку.

Друге правило: усе, що залежить від HTTP, лишається в контролері. Це означає: Request, FormRequest, query string, файли форми як об’єкти HTTP, редиректи, статус-коди, cookie, session flash, вибір view або JSON-відповіді. Сервіс не повинен знати, чи це прийшло з браузера, API, черги або консолі. Він має приймати звичайні дані (масив/DTO) і повертати результат (модель/структуру), не будучи прив’язаним до вебу.

Третє правило: валідація формальних правил — у FormRequest, бізнес-правила — у сервісі. Формальні правила — це “required/max/unique/date/in”. Їх зручно і правильно тримати в FormRequest. Бізнес-правила — це “не можна змінювати approval_status без ролі”, “після публікації не можна міняти ресурс”, “якщо ввімкнули subscription_required, перевір тариф”. Такі речі мають бути в сервісі, бо вони описують поведінку домену, а не форму.

Четверте правило: транзакції завжди в сервісі, якщо операція зачіпає більше ніж одну таблицю або має побічні ефекти. Контролер не повинен вирішувати, де починається і закінчується “атомарна” операція. Це відповідальність сервісу, бо він бачить весь сценарій: створили запис → синхронізували pivot → записали activity → оновили лічильник → відправили нотифікацію. Якщо щось падає посередині, сервіс повинен відкотити або коректно завершити операцію.

П’яте правило: “тонкий контролер, товстий сервіс” — але без перебору. Товстий сервіс — не означає “один метод на 500 рядків”. Правильний сервіс має 2–5 публічних методів під use-cases (create/update/delete/publish/approve) і кілька приватних методів для кроків. Контролер від цього виграє: кожен його метод перетворюється на 5–15 рядків читабельного коду.

Шосте правило: сервіс приймає лише те, що реально дозволено. Навіть якщо FormRequest віддав $validated, сервіс часто робить whitelist полів або явну мапу. Це особливо важливо для модулів з доступами: одні ролі можуть міняти одні поля, інші — інші. Контролер не повинен містити цю математику. Сервіс повинен сам визначати “ефективні” дані для запису, виходячи з ролі/політик/поточних станів.

Сьоме правило: робота з моделями і запитами — так, але не “все в контролері”. Запити на читання для списків (index) часто нормально залишити в контролері або винести в QueryBuilder/Repository, якщо стає складно. А от запис і сценарії зміни стану — це зона сервісу. Практично: index може зібрати фільтри і зробити paginate, але store/update мають делегувати створення/оновлення сервісу.

Восьме правило: побічні ефекти живуть поруч зі сценарієм, а не розкидані по коду. Побічні ефекти — це: activity log, нотифікації, черги, оновлення кешу, робота з файлами, інтеграції з соцмережами. Якщо вони розкидані по контролерах, ти не можеш гарантувати однакову поведінку. Якщо вони в сервісі, ти знаєш: “цей сценарій завжди пише activity і завжди валідно завершиться”.

Дев’яте правило: сервіс повертає результат, а форматування — в контролері/ресурсах. Якщо це web — контролер вирішує, який view і які flash. Якщо це API — контролер повертає JSON і статуси, можливо через Resource/Transformer. Сервіс не повинен повертати redirect() чи view(). Він повертає модель або DTO/масив з даними, і контролер вирішує, як це показати.

Де проходить “чітка межа” на прикладі CRUD. Контролер робить: взяв FormRequest → отримав $validated → викликав $service->create($validated, $actor) → редирект на index з with('success', ...). Сервіс робить: нормалізував → створив запис → при потребі транзакція → синхронізував зв’язки → записав activity → повернув модель. У такій схемі ти можеш легко додати ще один вхід: наприклад, CLI-команду для імпорту тегів — вона просто викличе той самий сервіс, і логіка буде однакова.

Типові анти-патерни, які треба відрізати одразу. Перший — “сервіс як обгортка над Model::create”. Якщо сервіс нічого не робить, він зайвий. Другий — “контролер виконує половину сценарію, сервіс — іншу половину”, коли логіка змішана. Третій — “сервіс приймає Request і повертає Response” — так ти знищуєш розділення шарів. Четвертий — “один сервіс на все” з десятками несумісних методів, де ніхто не розуміє, що відбувається.

Висновок: Service layer — це простий контракт. Контролер керує HTTP і UX (вхід/вихід), сервіс керує сценарієм і цілісністю даних (правила, транзакції, побічні ефекти). Якщо ти тримаєш цю межу, проєкт росте без болю: нові модулі додаються по одному шаблону, тести пишуться легше, а ризик “зламали існуюче” різко падає, бо вся логіка зосереджена в одному місці і працює однаково з будь-якого входу.


Максим Третяк — Кореспондент, який спеціалізується на суспільно важливих темах, пише про політику, фінансові ринки та економіку. Він проживає та працює в Україні.

Цей матеріал є частиною розгорнутої теми: Web-програмування, яка охоплює численні цікаві аспекти цієї події. Газета «Дейком» ретельно відстежує події, проводячи перевірку джерел та інформації, щоб забезпечити нашим читачам найбільш точне та актуальне інформування.

Цей матеріал опубліковано 01.01.2026 року о 12:00 GMT+3 Київ; 05:00 GMT-4 Вашингтон, розділ: Освіта, із заголовком: "Service layer у Laravel: чіткі правила, що лишається в контролері, а що йде в сервіс". Якщо в публікації з'являться зміни, про це буде зазначено та описано у кінці публікації.

Читайте щоденну газету та загальну стрічку новин газети Дейком, яка поєднує багато цікавого в понад 40 розділах з усіх куточків світу.


Save
ОГОЛОШЕННЯ

Новини, які можуть Вас зацікавити:

Штатні та позаштатні журналісти газети «Дейком» щодня готують сотні публікацій, щоб читачі отримували найоперативнішу, перевірену й глибоку інформацію. Ми працюємо для тих, хто хоче розуміти суть подій, бачити широку картину та бути на крок попереду.

Останні новини

Вибір редакції