Тема
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, SMSheavy— импорт, экспорт, генерация отчётов- Разделение предотвращает блокировку быстрых задач тяжёлыми
Events & Listeners
Когда использовать
- 3+ независимых реакций на одно действие (создание заказа → обновить статистику + отправить email + записать лог)
- Decoupling модулей, которые не должны знать друг о друге
Когда НЕ использовать
- Одна реакция на действие — просто вызов метода в сервисе
- Нужна гарантия порядка выполнения — listeners порядок не гарантируют
- Отладка важнее гибкости — прямой вызов проще дебажить
- Меньше 3 listeners — overengineering
Правила
- Event — только DTO (данные), без логики
- Listener — вызывает Service, не содержит логику сам
- Для async: listener implements
ShouldQueue
Notifications
Каналы
| Канал | Когда | Queue? |
|---|---|---|
| Важные: заявки, ошибки, подтверждения | Да, всегда | |
| database | История в админке, непрочитанные | Нет (быстрая запись) |
| Telegram (custom) | Мгновенные уведомления команде | Да |
| SMS (custom) | Критичные: OTP, подтверждение | Да |
Правила
- Всегда через queue (
ShouldQueue) кроме критичных (OTP, подтверждение) - Notification — только форматирование, логика — в Service
via()— осознанный выбор каналов, не "все сразу"- Текст уведомлений на русском для пользовательских, на английском для технических/dev
Общие anti-patterns
| Паттерн | Проблема | Решение |
|---|---|---|
| Синхронная отправка email в контроллере | Блокирует HTTP response на 2-5 сек | Job / Queue |
| Event ради одного listener | Overengineering | Прямой вызов |
Job без $tries / failed() | Silent failures, бесконечные retry | Всегда определять |
| Бизнес-логика в Job/Listener | Невозможно тестировать / переиспользовать | Job → Service |
| Notification без queue | Медленный response | ShouldQueue |
| Передача Collection/массива в Job | Serialization bloat, stale data | Передавать ID/модель |