PHP

Laravel Jobs, Queues e Events na Prática na Prática

9 min de leitura

Laravel Jobs, Queues e Events na Prática na Prática

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 . 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. O

<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">&lt;?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()-&gt;get(&#039;https://api.validate.com/orders&#039;, [

&#039;order_id&#039; =&gt; $this-&gt;order-&gt;id

]);

if ($response-&gt;successful()) {

$this-&gt;order-&gt;update([&#039;status&#039; =&gt; &#039;validated&#039;]);

\Mail::to($this-&gt;order-&gt;customer_email)-&gt;send(new OrderConfirmed($this-&gt;order));

}

}

public function failed(\Throwable $exception)

{

\Log::error(&#039;Job failed: &#039; . $exception-&gt;getMessage());

$this-&gt;order-&gt;update([&#039;status&#039; =&gt; &#039;failed&#039;]);

}

}</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-&gt;validated());

ProcessPurchaseOrder::dispatch($order);

return response()-&gt;json([&#039;message&#039; =&gt; &#039;Pedido recebido!&#039;], 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">&lt;?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">&lt;?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-&gt;user-&gt;email)-&gt;send(new WelcomeEmail($event-&gt;user));

}

}</code></pre>

<p>Registre listeners em <code>app/Providers/EventServiceProvider.php</code>:</p>

<pre><code class="language-php">protected $listen = [

UserRegistered::class =&gt; [

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-&gt;validated());

UserRegistered::dispatch($user);

return response()-&gt;json([&#039;user&#039; =&gt; $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(&#039;user_registered&#039;, [&#039;user_id&#039; =&gt; $event-&gt;user-&gt;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-&gt;report, $event-&gt;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-&gt;report-&gt;getComplexData();

$pdf = \PDF::loadView(&#039;reports.template&#039;, $data);

Storage::disk(&#039;s3&#039;)-&gt;put(

&quot;reports/{$this-&gt;report-&gt;id}.pdf&quot;,

$pdf-&gt;output()

);

ReportGenerated::dispatch($this-&gt;report, $this-&gt;user);

}

public function failed(\Throwable $exception)

{

\Notification::send($this-&gt;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-&gt;user,

new ReportReadyNotification($event-&gt;report)

);

}

}</code></pre>

<p>No controller, tudo é simples:</p>

<pre><code class="language-php">public function requestReport(Request $request)

{

$report = Report::create($request-&gt;validated());

ReportRequested::dispatch($report, auth()-&gt;user());

return response()-&gt;json([&#039;message&#039; =&gt; &#039;Relatório em processamento&#039;]);

}</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>

Comentários

Mais em PHP

Como Usar Conexão com MySQL usando PDO em PHP em Produção
Como Usar Conexão com MySQL usando PDO em PHP em Produção

Introdução ao PDO: Por que usar? PDO (PHP Data Objects) é uma abstração de ba...

Dominando Cache em PHP: Redis, Memcached e Cache de Opcode com OPcache em Projetos Reais
Dominando Cache em PHP: Redis, Memcached e Cache de Opcode com OPcache em Projetos Reais

Cache em PHP: Redis, Memcached e OPcache O cache é um dos pilares da otimizaç...

Dominando Traits em PHP: Reuso de Código sem Herança em Projetos Reais
Dominando Traits em PHP: Reuso de Código sem Herança em Projetos Reais

O que são Traits e Por Que Usá-las Traits em PHP são mecanismos que permitem...