LaravelMiddleSystem design

Что такое jobs, events и listeners в Laravel?

Jobs — единицы фоновой работы, отправляемые в очередь. Events — широковещательные уведомления о произошедшем. Listeners — обработчики конкретного события, могут реализовывать ShouldQueue для асинхронного выполнения.

Jobs (Задания)

Job — класс, инкапсулирующий одну единицу работы, которая выполняется синхронно или асинхронно через очередь. Создаётся командой php artisan make:job ProcessPayment.

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class ProcessPayment implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $timeout = 60;

    public function __construct(
        private readonly Order $order,
        private readonly string $paymentToken,
    ) {}

    public function handle(PaymentGateway $gateway): void
    {
        $gateway->charge($this->order, $this->paymentToken);
    }

    public function failed(\Throwable $e): void
    {
        // уведомить владельца заказа
        $this->order->user->notify(new PaymentFailed($e->getMessage()));
    }
}

// Отправка в очередь
ProcessPayment::dispatch($order, $token);

// С задержкой 5 минут
ProcessPayment::dispatch($order, $token)->delay(now()->addMinutes(5));

// Синхронно (без очереди)
ProcessPayment::dispatchSync($order, $token);

Events (События)

Event — простой класс-DTO, сигнализирующий о том, что что-то произошло в системе. Создаётся через php artisan make:event OrderPlaced. Событие само по себе ничего не делает — оно только несёт данные.

<?php

namespace App\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderPlaced
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public readonly Order $order,
    ) {}
}

// Диспетчеризация события
OrderPlaced::dispatch($order);
// или через хелпер
event(new OrderPlaced($order));

Listeners (Слушатели)

Listener — класс, реагирующий на конкретное событие. Создаётся через php artisan make:listener SendOrderConfirmation --event=OrderPlaced. Регистрируется в EventServiceProvider (Laravel 10) или через атрибут #[ListenTo] (Laravel 11+).

<?php

// Регистрация в App\Providers\EventServiceProvider
protected $listen = [
    OrderPlaced::class => [
        SendOrderConfirmation::class,
        UpdateInventory::class,
        NotifyWarehouse::class,
    ],
];

// Сам listener
class SendOrderConfirmation implements ShouldQueue
{
    public string $queue = 'notifications';

    public function handle(OrderPlaced $event): void
    {
        Mail::to($event->order->user)
            ->send(new OrderConfirmationMail($event->order));
    }
}

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

  • Job — изолированная фоновая задача с чёткой ответственностью: отправить email, обработать файл, вызвать внешний API. Один job = одна задача.
  • Event + Listeners — когда одно действие должно вызвать несколько независимых реакций (отправить письмо, обновить аналитику, уведомить Slack). Decoupling через события упрощает добавление новых реакций.
  • Listeners тоже реализуют ShouldQueue и выполняются асинхронно — это делает их функционально схожими с jobs, но с семантикой «реакция на событие».

Подводные камни

  • Сериализация Eloquent-моделей — трейт SerializesModels сохраняет только primary key и перезагружает модель при выполнении. Если модель удалена к тому моменту — job упадёт с ModelNotFoundException. Проверяйте существование в handle().
  • Listeners без ShouldQueue блокируют запрос — если слушатель выполняет медленную операцию синхронно, время ответа HTTP-запроса увеличивается на всё это время.
  • Порядок listeners не гарантирован при параллельных воркерах — если несколько listeners одного события попадают в очередь, они могут выполниться в любом порядке.
  • Дублирование при failed job retry — job, упавший после частичного выполнения (например, деньги списаны, но письмо не отправлено), при повторном запуске может задублировать операцию. Используйте идемпотентность через уникальный ключ или проверку статуса.
  • Job chaining прерывается при ошибкеBus::chain([...])->dispatch() останавливается на первом упавшем job, последующие не выполняются.

Common mistakes

  • Сводить jobs events listeners к названию метода без lifecycle и failure path.
  • Игнорировать модель runtime: Laravel 13 поверх PHP: request проходит через HTTP kernel, middleware, routing, controller/action и response pipeline.
  • Не отделять validation, authorization, transaction boundary и business logic.
  • Не обсуждать idempotency, retries, shutdown и observability.

What the interviewer is testing

  • Объясняет jobs events listeners через конкретную точку lifecycle в Laravel.
  • Приводит корректный минимальный пример без вымышленных методов или callbacks.
  • Называет edge cases: пустые значения, ошибки, транзакции, безопасность или concurrency.
  • Связывает решение с метриками, backpressure, retry policy и graceful shutdown.

Sources

Related topics