Entendendo Jobs, Queues e Events no Laravel
Jobs, queues e events são pilares para construir aplicações Laravel escaláveis e responsivas. Um Job é uma unidade de trabalho que pode ser executada de forma assíncrona, fora do ciclo de requisição HTTP. Queues (filas) armazenam e processam esses jobs em background, enquanto Events permitem que você despache ações que múltiplos listeners podem responder. A combinação desses três conceitos resolve problemas reais: enviar 10 mil e-mails sem travar a aplicação, processar uploads pesados e manter o código desacoplado e testável.
Nesta aula, você aprenderá não apenas como implementar, mas quando usar cada ferramenta. Vamos trabalhar com exemplos práticos que funcionam imediatamente em seu projeto Laravel.
Jobs e Queues na Prática
Criando e Despachando um Job
Um job é criado com o comando make:job. Imagine que você precisa processar um pedido de compra que envolve validações externas, envio de e-mail e atualização de inventário. Essa tarefa não deve bloquear a resposta ao cliente.
php artisan make:job ProcessPurchaseOrder
O job gerado fica em app/Jobs/ProcessPurchaseOrder.php:
<?php
namespace App\Jobs;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPurchaseOrder implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public Order $order
) {}
public function handle()
{
// Validar com API externa
$response = httpClient()->get('https://api.validate.com/orders', [
'order_id' => $this->order->id
]);
if ($response->successful()) {
$this->order->update(['status' => 'validated']);
\Mail::to($this->order->customer_email)->send(new OrderConfirmed($this->order));
}
}
public function failed(\Throwable $exception)
{
\Log::error('Job failed: ' . $exception->getMessage());
$this->order->update(['status' => 'failed']);
}
}
Para despachar o job de forma assíncrona:
// No controller
public function store(Request $request)
{
$order = Order::create($request->validated());
ProcessPurchaseOrder::dispatch($order);
return response()->json(['message' => 'Pedido recebido!'], 202);
}
Configurando a Queue
Por padrão, Laravel usa o driver sync que executa jobs imediatamente. Para verdadeiro processamento em background, configure o driver database ou redis em .env:
QUEUE_CONNECTION=database
Crie a tabela de jobs com: php artisan queue:table e php artisan migrate.
Inicie um worker para processar jobs:
php artisan queue:work
Este comando fica em loop, aguardando novos jobs na fila. Em produção, use supervisord ou similares para manter o worker ativo.
Events e Listeners para Desacoplamento
O Poder da Arquitetura Event-Driven
Events desacoplam sua lógica. Em vez de colocar tudo no controller, você dispara um evento e múltiplos listeners reagem. Imagine: quando um usuário se registra, você precisa enviar e-mail de boas-vindas, registrar no CRM, e atualizar analytics. Sem events, seu controller vira um caos. Com events, cada responsabilidade fica isolada.
php artisan make:event UserRegistered
php artisan make:listener SendWelcomeEmail --event=UserRegistered
php artisan make:listener LogToAnalytics --event=UserRegistered
O event em app/Events/UserRegistered.php:
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public User $user) {}
}
Listener que envia e-mail em background:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeEmail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Mail;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)->send(new WelcomeEmail($event->user));
}
}
Registre listeners em app/Providers/EventServiceProvider.php:
protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
LogToAnalytics::class,
],
];
Dispache o event do seu controller:
public function register(Request $request)
{
$user = User::create($request->validated());
UserRegistered::dispatch($user);
return response()->json(['user' => $user], 201);
}
Listeners com Queue vs Síncronos
Por padrão, listeners executam no mesmo processo. Implemente ShouldQueue no listener para executá-lo em background:
class LogToAnalytics implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// Será enfileirado automaticamente
Analytics::track('user_registered', ['user_id' => $event->user->id]);
}
}
Integrando Jobs, Queues e Events
Caso de Uso Real: Processamento de Relatório
Combine os três conceitos para um cenário realista. Um usuário solicita um relatório PDF grande:
// Event disparado quando relatório é solicitado
php artisan make:event ReportRequested
class ReportRequested
{
public function __construct(public Report $report, public User $user) {}
}
Listener que despacha o job:
class GenerateReportPdf implements ShouldQueue
{
public function handle(ReportRequested $event): void
{
GenerateReport::dispatch($event->report, $event->user);
}
}
Job que processa pesadamente:
class GenerateReport implements ShouldQueue
{
public $timeout = 300; // 5 minutos
public function __construct(
protected Report $report,
protected User $user
) {}
public function handle()
{
$data = $this->report->getComplexData();
$pdf = \PDF::loadView('reports.template', $data);
Storage::disk('s3')->put(
"reports/{$this->report->id}.pdf",
$pdf->output()
);
ReportGenerated::dispatch($this->report, $this->user);
}
public function failed(\Throwable $exception)
{
\Notification::send($this->user, new ReportFailedNotification());
}
}
Listener final que notifica o usuário:
class NotifyReportReady
{
public function handle(ReportGenerated $event): void
{
\Notification::send(
$event->user,
new ReportReadyNotification($event->report)
);
}
}
No controller, tudo é simples:
public function requestReport(Request $request)
{
$report = Report::create($request->validated());
ReportRequested::dispatch($report, auth()->user());
return response()->json(['message' => 'Relatório em processamento']);
}
Conclusão
Os três pilares — Jobs para executar trabalho assíncrono, Queues para gerenciar fila de processamento e Events para desacoplar lógica — transformam aplicações Laravel em sistemas responsivos e escaláveis. Use jobs quando há trabalho pesado que não precisa bloquear o usuário. Implemente events quando múltiplos componentes precisam reagir ao mesmo acontecimento. Configure queues com Redis em produção para performance máxima. A arquitetura correta desde o início economiza refatorações futuras.