<h2>Entendendo Jobs, Queues e Events no Laravel</h2>
<p>Jobs, queues e events são pilares para construir aplicações Laravel escaláveis e responsivas. Um <strong>Job</strong> é uma unidade de trabalho que pode ser executada de forma assíncrona, fora do ciclo de requisição HTTP. <strong>Queues</strong> (filas) armazenam e processam esses jobs em background, enquanto <strong>Events</strong> 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.</p>
<p>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.</p>
<h2>Jobs e Queues na Prática</h2>
<h3>Criando e Despachando um Job</h3>
<p>Um job é criado com o comando <code>make:job</code>. 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.</p>
<pre><code class="language-bash">php artisan make:job ProcessPurchaseOrder</code></pre>
<p>O job gerado fica em <code>app/Jobs/ProcessPurchaseOrder.php</code>:</p>
<pre><code class="language-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']);
}
}</code></pre>
<p>Para despachar o job de forma assíncrona:</p>
<pre><code class="language-php">// No controller
public function store(Request $request)
{
$order = Order::create($request->validated());
ProcessPurchaseOrder::dispatch($order);
return response()->json(['message' => 'Pedido recebido!'], 202);
}</code></pre>
<h3>Configurando a Queue</h3>
<p>Por padrão, Laravel usa o driver <code>sync</code> que executa jobs imediatamente. Para verdadeiro processamento em background, configure o driver <code>database</code> ou <code>redis</code> em <code>.env</code>:</p>
<pre><code class="language-env">QUEUE_CONNECTION=database</code></pre>
<p>Crie a tabela de jobs com: <code>php artisan queue:table</code> e <code>php artisan migrate</code>.</p>
<p>Inicie um worker para processar jobs:</p>
<pre><code class="language-bash">php artisan queue:work</code></pre>
<p>Este comando fica em loop, aguardando novos jobs na fila. Em produção, use supervisord ou similares para manter o worker ativo.</p>
<h2>Events e Listeners para Desacoplamento</h2>
<h3>O Poder da Arquitetura Event-Driven</h3>
<p>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.</p>
<pre><code class="language-bash">php artisan make:event UserRegistered
php artisan make:listener SendWelcomeEmail --event=UserRegistered
php artisan make:listener LogToAnalytics --event=UserRegistered</code></pre>
<p>O event em <code>app/Events/UserRegistered.php</code>:</p>
<pre><code class="language-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) {}
}</code></pre>
<p>Listener que envia e-mail em background:</p>
<pre><code class="language-php"><?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));
}
}</code></pre>
<p>Registre listeners em <code>app/Providers/EventServiceProvider.php</code>:</p>
<pre><code class="language-php">protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
LogToAnalytics::class,
],
];</code></pre>
<p>Dispache o event do seu controller:</p>
<pre><code class="language-php">public function register(Request $request)
{
$user = User::create($request->validated());
UserRegistered::dispatch($user);
return response()->json(['user' => $user], 201);
}</code></pre>
<h3>Listeners com Queue vs Síncronos</h3>
<p>Por padrão, listeners executam no mesmo processo. Implemente <code>ShouldQueue</code> no listener para executá-lo em background:</p>
<pre><code class="language-php">class LogToAnalytics implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// Será enfileirado automaticamente
Analytics::track('user_registered', ['user_id' => $event->user->id]);
}
}</code></pre>
<h2>Integrando Jobs, Queues e Events</h2>
<h3>Caso de Uso Real: Processamento de Relatório</h3>
<p>Combine os três conceitos para um cenário realista. Um usuário solicita um relatório PDF grande:</p>
<pre><code class="language-php">// Event disparado quando relatório é solicitado
php artisan make:event ReportRequested</code></pre>
<pre><code class="language-php">class ReportRequested
{
public function __construct(public Report $report, public User $user) {}
}</code></pre>
<p>Listener que despacha o job:</p>
<pre><code class="language-php">class GenerateReportPdf implements ShouldQueue
{
public function handle(ReportRequested $event): void
{
GenerateReport::dispatch($event->report, $event->user);
}
}</code></pre>
<p>Job que processa pesadamente:</p>
<pre><code class="language-php">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());
}
}</code></pre>
<p>Listener final que notifica o usuário:</p>
<pre><code class="language-php">class NotifyReportReady
{
public function handle(ReportGenerated $event): void
{
\Notification::send(
$event->user,
new ReportReadyNotification($event->report)
);
}
}</code></pre>
<p>No controller, tudo é simples:</p>
<pre><code class="language-php">public function requestReport(Request $request)
{
$report = Report::create($request->validated());
ReportRequested::dispatch($report, auth()->user());
return response()->json(['message' => 'Relatório em processamento']);
}</code></pre>
<h2>Conclusão</h2>
<p>Os três pilares — <strong>Jobs para executar trabalho assíncrono</strong>, <strong>Queues para gerenciar fila de processamento</strong> e <strong>Events para desacoplar lógica</strong> — 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://laravel.com/docs/queues" target="_blank" rel="noopener noreferrer">Laravel Documentation - Queues</a></li>
<li><a href="https://laravel.com/docs/events" target="_blank" rel="noopener noreferrer">Laravel Documentation - Events</a></li>
<li><a href="https://laravel.com/docs/cache#redis" target="_blank" rel="noopener noreferrer">Redis Configuration for Laravel</a></li>
<li><a href="https://laravel.com/docs/queues#supervisor-configuration" target="_blank" rel="noopener noreferrer">Supervisord Setup for Queue Workers</a></li>
<li><a href="https://martinfowler.com/articles/201701-event-driven.html" target="_blank" rel="noopener noreferrer">Event-Driven Architecture Patterns</a></li>
</ul>