Laravel’de Event-Driven Mimari: Event, Listener ve Subscriber ile Gevşek Bağlı Tasarım
Laravel’in event sistemiyle modüler, test edilebilir ve genişletilebilir bir yapı kurmanın pratik yolları.
Neden Event-Driven?
Uygulama büyüdükçe Controller -> Service -> ... zinciri kalabalıklaşır; bir işlem (ör. sipariş oluşturma) yeni yan etkiler (e-posta, stok düşme, analitik, bildirim) kazandıkça mevcut akışa sürekli kod eklenir. Event-driven yaklaşım, ana iş akışını sade tutar; yan etkileri ayrı listener’lara böler.
Amaç: “Sipariş oluştu” bilgisini yayınla, gerisini aboneler (listener/subscriber) halletsin.
Basit Senaryo: Sipariş Oluştu
1) Event tanımı
php artisan make:event OrderPlaced
// app/Events/OrderPlaced.php
namespace App\Events;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderPlaced
{
use Dispatchable, SerializesModels;
public function __construct(public Order $order) {}
}
2) Listener’lar
Örn: stok düş, e-posta at, analitik kaydet.
php artisan make:listener DecreaseStock --event=OrderPlaced
php artisan make:listener SendOrderConfirmationEmail --event=OrderPlaced
// app/Listeners/DecreaseStock.php
namespace App\Listeners;
use App\Events\OrderPlaced;
class DecreaseStock
{
public function handle(OrderPlaced $event): void
{
foreach ($event->order->items as $item) {
$item->product->decrement('stock', $item->quantity);
}
}
}
3) Event’i tetiklemek
Sipariş kaydedildiği yerde:
use App\Events\OrderPlaced;
$order = Order::create([...]);
OrderPlaced::dispatch($order);
Bu kadar: Sipariş akışını, yan etkilerden bağımsız tutmuş oldun.
Listener’ları Kuyruğa Al: Performans + Dayanıklılık
Bazı işler (mail, üçüncü parti API, raporlama) yavaş olabilir. Listener’ı queue üzerinden çalıştırmak için ShouldQueue kullan.
namespace App\Listeners;
use App\Events\OrderPlaced;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendOrderConfirmationEmail implements ShouldQueue
{
public int $tries = 3;
public function handle(OrderPlaced $event): void
{
// Mail::to(...)->send(...)
}
}
Bu yaklaşım, kullanıcıya yanıt süresini kısaltır ve geçici hatalarda otomatik tekrar denemeye imkan verir.
Subscriber Kullan: Aynı Modülün Event’lerini Topla
Listener sayısı arttığında dosya karmaşası oluşabilir. Bir modülün event’lerini tek yerde toplamak için subscriber kullan.
php artisan make:listener OrderSubscriber
// app/Listeners/OrderSubscriber.php
namespace App\Listeners;
use App\Events\OrderPlaced;
use Illuminate\Events\Dispatcher;
class OrderSubscriber
{
public function onOrderPlaced(OrderPlaced $event): void
{
// örn: internal metrics
}
public function subscribe(Dispatcher $events): void
{
$events->listen(OrderPlaced::class, [self::class, 'onOrderPlaced']);
}
}
EventServiceProvider içine ekle:
protected $subscribe = [
\App\Listeners\OrderSubscriber::class,
];
İyi Pratikler (Kısa Notlar)
- Event adları geçmiş zamanda olsun:
OrderPlaced,UserRegistered. - Event içine gereksiz veri şişirme: genelde
idveya ilgili model yeterli. - Listener’lar tek sorumluluk taşısın: “stok düşmek” ile “mail atmak” aynı listener olmasın.
- Kritik yan etkiler için idempotency düşün: aynı event iki kez işlenirse stok iki kez düşmesin (ör. işlem kaydı tut).
Test Etmesi Kolay
Laravel, event’leri testte kolayca “fake” etmeyi sağlar:
use Illuminate\Support\Facades\Event;
use App\Events\OrderPlaced;
Event::fake();
// sipariş oluşturma aksiyonu...
Event::assertDispatched(OrderPlaced::class);
Bu sayede “sipariş oluşunca event fırlıyor mu?” sorusunu hızlıca doğrularsın.
Sonuç
Event-driven yaklaşım, Laravel projelerinde kodu modülerleştirir, ana akışı sadeleştirir ve yeni özellikleri “mevcut koda dokunmadan” eklemeyi kolaylaştırır. Küçük başlayıp (1 event + 1 listener) büyüterek ilerlemek en sağlıklısıdır.