Skip to content

Services, Jobs, Events, Notifications

Services

Когда создавать

  • Логика используется в 2+ контроллерах
  • Метод контроллера > 10 строк логики
  • Нужна DB-транзакция (несколько записей атомарно)
  • Есть побочные эффекты (email, telegram, внешний API)
  • Нужно тестировать логику изолированно от HTTP

Правила

  • Один сервис = одна доменная область (OrderService, ContactService, SeoService)
  • DI через конструктор — не app() / resolve() внутри методов
  • DB::transaction() — когда несколько записей должны быть атомарны
  • Сервис НЕ знает про Request / Response / View — работает только с данными
  • Возвращает модели, коллекции, массивы, скаляры — не Response
  • Не более 300 строк — если больше, разбивать по ответственности

Регистрация в контейнере

  • Простые сервисы: Laravel резолвит через auto-wiring, регистрация не нужна
  • Интерфейс → реализация: bind в AppServiceProvider::register()
  • Singleton — только когда состояние должно жить весь request lifecycle

Anti-patterns сервисов

ПаттернПроблемаРешение
God Service (1000+ строк)Нарушает SRPРазбить по доменам
app(Service::class) внутри методаСкрытая зависимостьConstructor DI
Сервис возвращает ResponseЗнает про HTTPВозвращать данные
Сервис читает request()Связь с HTTP-слоемПринимать данные аргументами

Jobs (Queue)

Когда использовать

  • Отправка email / telegram / SMS — не блокировать HTTP response
  • Обработка файлов (resize, import, export CSV)
  • Вызов внешних API (могут быть медленными / падать)
  • Тяжёлые вычисления (отчёты, генерация PDF)

Правила

  • $tries — ВСЕГДА указывать (сколько попыток)
  • $backoff — ВСЕГДА указывать (задержка между попытками)
  • failed() — ВСЕГДА определять (обработка финального провала)
  • Передавать в Job только ID или модель (SerializesModels) — не массивы данных
  • Job НЕ возвращает результат в HTTP — если нужен ответ, делай синхронно
  • Бизнес-логика — в Service, Job только вызывает Service

Очереди

  • default — общая очередь
  • notifications — email, telegram, SMS
  • heavy — импорт, экспорт, генерация отчётов
  • Разделение предотвращает блокировку быстрых задач тяжёлыми

Events & Listeners

Когда использовать

  • 3+ независимых реакций на одно действие (создание заказа → обновить статистику + отправить email + записать лог)
  • Decoupling модулей, которые не должны знать друг о друге

Когда НЕ использовать

  • Одна реакция на действие — просто вызов метода в сервисе
  • Нужна гарантия порядка выполнения — listeners порядок не гарантируют
  • Отладка важнее гибкости — прямой вызов проще дебажить
  • Меньше 3 listeners — overengineering

Правила

  • Event — только DTO (данные), без логики
  • Listener — вызывает Service, не содержит логику сам
  • Для async: listener implements ShouldQueue

Notifications

Каналы

КаналКогдаQueue?
mailВажные: заявки, ошибки, подтвержденияДа, всегда
databaseИстория в админке, непрочитанныеНет (быстрая запись)
Telegram (custom)Мгновенные уведомления командеДа
SMS (custom)Критичные: OTP, подтверждениеДа

Правила

  • Всегда через queue (ShouldQueue) кроме критичных (OTP, подтверждение)
  • Notification — только форматирование, логика — в Service
  • via() — осознанный выбор каналов, не "все сразу"
  • Текст уведомлений на русском для пользовательских, на английском для технических/dev

Общие anti-patterns

ПаттернПроблемаРешение
Синхронная отправка email в контроллереБлокирует HTTP response на 2-5 секJob / Queue
Event ради одного listenerOverengineeringПрямой вызов
Job без $tries / failed()Silent failures, бесконечные retryВсегда определять
Бизнес-логика в Job/ListenerНевозможно тестировать / переиспользоватьJob → Service
Notification без queueМедленный responseShouldQueue
Передача Collection/массива в JobSerialization bloat, stale dataПередавать ID/модель

Внутренняя база знаний студии