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

“Тонкий контролер, товстий сервіс” у Laravel: як будувати код, який не розвалиться через місяць

Практичний стандарт: що залишати в контролері, що переносити в сервіс, як називати методи, як тримати транзакції/валідацію/доступи в правильних місцях і не перетворити проєкт на “спагеті”.


Save
Максим Третяк
Від
Максим Третяк
Газета Дейком | 01.02.2026, 15:30 GMT+3; 08:30 GMT-4
Мова публікації: Українська

Ідея “тонкий контролер, товстий сервіс” потрібна не для краси, а щоб система залишалась керованою. Контролер — це HTTP-шар: прийняти запит, перевірити доступ, викликати бізнес-дію, повернути відповідь. Коли в контролері з’являються 200 рядків логіки, ви отримуєте три проблеми: важко тестувати, важко повторно використати, і дуже легко випадково зламати існуючі обмеження. Сервісний шар існує, щоб бізнес-логіка була в одному місці, а контролери залишались простими і передбачуваними.

Що саме має бути в “тонкому” контролері. Перше — отримання параметрів маршруту та запиту (але не їхня складна обробка). Друге — авторизація: policy/gate/middleware. Третє — валідація через FormRequest (контролер отримує вже валідовані дані). Четверте — один виклик сервісу, який робить дію. П’яте — повернення відповіді: redirect або view або json. Все. Якщо контролер починає сам будувати складні query, робити транзакції, логувати activity, дергати storage, рахувати суми, розносити платежі — це вже не контролер, це “кухня” в неправильному місці.

Що має бути в “товстому” сервісі. У сервісі живе бізнес-операція: створити/оновити документ, змінити статус, провести, розпровести, опублікувати, відправити на модерацію, підв’язати медіа, перерахувати агрегати. Сервіс відповідає за порядок кроків, за транзакцію, за цілісність даних між кількома таблицями, за доменні правила (“не можна опублікувати без slug”, “не можна провести без складу”, “не можна змінити суму у posted”). Якщо операція торкається 2+ таблиць — це майже гарантовано має бути сервіс з транзакцією.

Далі ключове: “зрозумілі методи” — це про те, як ви називаєте і структуруєте сервіси. Погані назви — це коли все називається handle() або process() або doSomething(). Хороші — коли метод читається як дія: createDraft(), updateDetails(), submitForReview(), approve(), publish(), cancel(), post(), unpost(), attachMedia(). Тоді контролер виглядає як сценарій, а не як код-каша: “перевірив доступ → викликав publish → відредіректив”.

Дуже важлива дисципліна: один метод сервісу = одна бізнес-операція з чітким результатом. Не робіть метод, який “і створює, і відправляє, і постить, і ще трошки”. Краще дві окремі операції, якщо вони реально різні, або одна операція, але всередині — приватні кроки з зрозумілими назвами. Це дає читабельність і контроль.

Як правильно передавати дані в сервіс. Контролер не має передавати “весь Request”. Він має передати вже валідований масив або DTO. Це важливо, бо сервіс тоді не залежить від HTTP і його легше тестувати. Контролер може взяти validated() з FormRequest і передати. Далі сервіс приводить дані до нормального формату (нормалізація) і працює з ними як з бізнес-входом, а не як з “сирим” запитом.

Де має бути транзакція. Транзакція майже завжди повинна бути в сервісі, не в контролері. Бо транзакція — це частина бізнес-операції (“або все записалось узгоджено, або нічого”). Якщо транзакцію залишити в контролері, ви ризикуєте, що хтось інший викличе сервіс в іншому місці і забуде обгорнути транзакцією. У результаті — роз’їхались таблиці. Тому правило: якщо сервіс пише в 2+ таблиці або змінює критичний стан — транзакція всередині сервісу.

Де має бути лог активності й нотифікації. Activity log — у сервісі, синхронно, як частина факту зміни стану. Нотифікації — як побічний ефект: сервіс може диспатчити job або кидати domain event, який підхопить listener. Важливо: нотифікації не мають бути причиною провалу основної транзакції, якщо це не критично. Тобто “запис зберегли”, а “email не пішов” — це окремий інцидент, не відкат бізнес-операції.

Як це виглядає у типових контролерах. Index-контролер: отримав фільтри → передав у QueryFilter або сервіс “list” → повернув view. Store/update: прийняв FormRequest → викликав сервіс create/update → redirect. Change status: викликав publish/approve/cancel → redirect. Тобто контролер стає сценарієм, а не місцем, де “варять” бізнес-логіку.

Чому це важливо саме для навчання. Новачок, який пише “все в контролері”, швидко робить “щось працює”, але потім він не може підтримувати. Кожна нова фіча — це ризик зламати старі фільтри/доступи/валідацію. Коли логіка у сервісі, ви можете: повторно використати її в CLI/cron/jobs, легко тестувати, і робити акуратні розширення, не ламаючи контракт.

Найчастіші помилки в цьому патерні. Перша — зробити “GodService”, який робить все на світі. Це така сама проблема, як “GodController”, тільки в іншому файлі. Лікується розбиттям сервісів по доменних операціях або сутностях. Друга — залишити частину правил у контролері, частину в сервісі, частину в Blade. Це створює неузгодженість. Третя — передавати в сервіс невалідовані дані і потім ловити баги “інколи падає”. Тому FormRequest і нормалізація — обов’язкові.

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


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

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

Цей матеріал опубліковано 01.02.2026 року о 15:30 GMT+3 Київ; 08:30 GMT-4 Вашингтон, розділ: Освіта, із заголовком: "“Тонкий контролер, товстий сервіс” у Laravel: як будувати код, який не розвалиться через місяць". Якщо в публікації з'являться зміни, про це буде зазначено та описано у кінці публікації.

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


Save
ОГОЛОШЕННЯ

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

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

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

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

Європейські новини: