Events & Queues Demonstrator
Learn how to build robust, performant applications using Events and Queues
When a user registers on your platform, you typically need to:
• Send a welcome email
• Update your CRM system
• Generate analytics events
• Create initial user settings
Running all these tasks during the HTTP request makes your application slow and fragile. If the CRM API is down, registration fails!
The Solution: Event-Driven Architecture with Queues
1. Fire an event when something happens (UserRegistered)
2. Create listeners that respond to this event
3. Make listeners queueable so they run in the background
4. The user gets an instant response, and tasks process asynchronously
Real-World Impact:
• Response time: 2500ms → 150ms
• User experience: Waiting → Instant
• Reliability: If email fails, registration still succeeds
• Scalability: Handle 1000 signups/minute easily
• Send a welcome email
• Update your CRM system
• Generate analytics events
• Create initial user settings
Running all these tasks during the HTTP request makes your application slow and fragile. If the CRM API is down, registration fails!
The Solution: Event-Driven Architecture with Queues
1. Fire an event when something happens (UserRegistered)
2. Create listeners that respond to this event
3. Make listeners queueable so they run in the background
4. The user gets an instant response, and tasks process asynchronously
Real-World Impact:
• Response time: 2500ms → 150ms
• User experience: Waiting → Instant
• Reliability: If email fails, registration still succeeds
• Scalability: Handle 1000 signups/minute easily
Code Examples
Synchronous Approach (Slow & Fragile)
✗ Avoid This
php
<?php
// BAD: Everything runs during the HTTP request
class RegistrationController extends Controller
{
public function register(Request $request)
{
$startTime = microtime(true);
// Create user in database (50ms)
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
// Send welcome email via SMTP (800ms)
Mail::to($user->email)->send(new WelcomeEmail($user));
// Update CRM via API call (1200ms)
$crmClient = new CRMClient();
$crmClient->createContact([
'email' => $user->email,
'name' => $user->name,
'source' => 'website_registration',
]);
// Track analytics (300ms)
Analytics::track('user_registered', [
'user_id' => $user->id,
'plan' => 'free',
]);
// Generate welcome dashboard (150ms)
DashboardGenerator::createDefault($user);
$totalTime = (microtime(true) - $startTime) * 1000;
// Total time: ~2500ms
return response()->json([
'message' => 'Registration successful',
'user' => $user,
'processing_time' => "{$totalTime}ms"
]);
}
}
// Problems:
// ❌ Slow: User waits 2.5 seconds
// ❌ Fragile: If CRM is down, entire registration fails
// ❌ Poor UX: User sees loading spinner for ages
// ❌ Timeout risk: May hit 30s PHP timeout on slow connections
// ❌ Not scalable: Can't handle traffic spikes
💡 This approach runs all tasks synchronously. The user must wait for emails to send, APIs to respond, and analytics to track before getting a response. If any external service is slow or down, the entire registration fails.
Event-Driven with Queues (Fast & Robust)
✓ Best Practice
php
<?php
// GOOD: Event-driven architecture with queued listeners
// 1. Define the Event
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, SerializesModels;
public function __construct(public User $user) {}
}
// 2. Create Queued Listeners
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)
->send(new WelcomeEmail($event->user));
}
}
class UpdateCRM implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
$crmClient = new CRMClient();
$crmClient->createContact([
'email' => $event->user->email,
'name' => $event->user->name,
]);
}
}
class TrackRegistrationAnalytics implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
Analytics::track('user_registered', [
'user_id' => $event->user->id,
]);
}
}
// 3. Simple Controller
class RegistrationController extends Controller
{
public function register(Request $request)
{
$startTime = microtime(true);
// Only the essential: Create user (50ms)
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
// Fire event - listeners execute in background
UserRegistered::dispatch($user);
$totalTime = (microtime(true) - $startTime) * 1000;
// Total time: ~150ms (16x faster!)
return response()->json([
'message' => 'Registration successful',
'user' => $user,
'processing_time' => "{$totalTime}ms"
]);
}
}
// Benefits:
// ✅ Fast: Response in 150ms
// ✅ Resilient: If CRM fails, user still registers
// ✅ Great UX: Instant feedback
// ✅ Scalable: Queue workers handle spikes
// ✅ Retry logic: Failed jobs auto-retry
💡 The controller only creates the user and fires an event. All slow tasks (email, CRM, analytics) are handled by queued listeners running in the background. The user gets an instant response, and the application is resilient to external failures.
Register Event Listeners
💡 Solution
php
<?php
// In EventServiceProvider.php
namespace App\Providers;
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
use App\Listeners\UpdateCRM;
use App\Listeners\TrackRegistrationAnalytics;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
UpdateCRM::class,
TrackRegistrationAnalytics::class,
],
];
}
// Run queue worker to process jobs:
// php artisan queue:work
// Monitor failed jobs:
// php artisan queue:failed
// Retry failed jobs:
// php artisan queue:retry all
💡 Register your event listeners in the EventServiceProvider. When UserRegistered is fired, all three listeners are pushed to the queue and processed by background workers.