Setiap aksi penting dalam aplikasi jarang berdiri sendiri. Saat pelanggan menyelesaikan pesanan, aplikasi tidak hanya menyimpan data order — aplikasi juga perlu mengirim email konfirmasi, mencatat statistik penjualan, menyiapkan invoice, dan memberi tahu tim gudang. Jika semua pekerjaan itu ditulis berurutan di satu method, method tersebut menjadi rapuh: satu langkah gagal, seluruh proses ikut gagal; satu efek baru ditambahkan, method utama harus disunting lagi. Section ini membahas pola event-driven yang Laravel sediakan untuk memisahkan aksi utama dari efek-efek ikutannya, seperti dijelaskan pada dokumentasi Events .
Masalah Tight Coupling #
Perhatikan controller checkout berikut yang menangani semua efek secara langsung:
public function store(Request $request)
{
$data = $request->validate([
'email' => ['required', 'email'],
'items' => ['required', 'array'],
]);
$order = Order::create($data);
// Efek ikutan #1 sampai #4, semuanya dipanggil langsung:
Mail::to($order->email)->send(new OrderConfirmation($order));
Cache::increment('statistik:penjualan');
$akuntansi->buatInvoice($order);
$notifikasiGudang->peringatkan($order);
return redirect()->route('orders.show', $order);
}Method ini tampak bekerja, tetapi menyimpan tiga masalah struktural. Pertama, controller tahu terlalu banyak: ia mengenal mailer, cache, service akuntansi, dan notifikasi gudang sekaligus — inilah yang disebut tight coupling. Kedua, controller menjadi tidak bisa diuji secara terisolasi karena setiap pemanggilan store() menyeret seluruh dependensi ikutannya. Ketiga, efek-efek itu berjalan sinkron: bila service akuntansi lambat, respons HTTP ke pelanggan ikut lambat.
Event: Fakta yang Sudah Terjadi #
Pola event-driven membalik pendekatannya. Alih-alih controller memanggil setiap efek, controller cukup mengumumkan satu fakta: “sebuah pesanan baru saja dibuat”. Fakta itu dibungkus dalam event — object kecil yang membawa data konteksnya. Konvensi penamaannya konsisten: event selalu past tense (bentuk lampau) karena event adalah kejadian yang sudah terjadi, bukan perintah yang belum dieksekusi.
Di baliknya berdiri pola klasik dari dunia object-oriented programming: observer pattern. Subjek (pengirim event) tidak menyimpan daftar pihak yang harus dihubungi satu per satu; ia hanya berteriak sekali, dan siapa pun yang berlangganan akan mendengar. Laravel mengambil pola ini dan membungkusnya menjadi dispatcher yang siap pakai — event tidak pernah mengenal listener, dan listener hanya mengenal event.
| Nama event | Maknanya |
|---|---|
OrderPlaced | pesanan baru saja dibuat |
PaymentSucceeded | pembayaran berhasil diverifikasi |
OrderShipped | paket pesanan sudah diserahkan ke kurir |
UserRegistered | akun pengguna baru saja terdaftar |
Listener: Reaksi atas Fakta #
Listener berada di sisi sebaliknya. Listener adalah class atau closure yang bereaksi terhadap event tertentu — mengirim email, mencatat statistik, atau mendorong pekerjaan berat ke antrean. Pengirim event tidak pernah tahu listener siapa saja yang mendengarkan; sebaliknya, listener hanya bergantung pada event, bukan pada controller yang mengirimkannya. Hubungan kedua pihak menjadi longgar (loose coupling) karena satu-satunya hal yang mereka kenal bersama adalah event itu sendiri.
Alur lengkapnya terlihat seperti ini:
Controller hanya melakukan satu aksi utama (menyimpan order) lalu melempar satu event. Dispatcher membagikan event itu ke semua listener yang terdaftar, dan setiap listener menjalankan reaksinya sendiri — sinkron di request yang sama, atau lewat queue untuk pekerjaan lambat.
Memisahkan Aksi Utama dari Efek Ikatannya #
Dengan pola ini, controller di atas boleh dirampingkan menjadi berikut:
public function store(Request $request)
{
$data = $request->validate([
'email' => ['required', 'email'],
'items' => ['required', 'array'],
]);
$order = Order::create($data);
OrderPlaced::dispatch($order->id, $order->email, $order->total);
return redirect()->route('orders.show', $order);
}Empat baris efek ikutan tadi lenyap, digantikan satu baris dispatch. Menambah efek baru — misalnya menghitung poin loyalitas — kini berarti menulis listener baru tanpa menyentuh controller sama sekali. Efek yang gagal juga tidak lagi membawa runtuh seluruh checkout, karena tiap listener berjalan (dan gagal) secara terpisah.
Kapan Tidak Perlu Event #
Pola ini bukan obat untuk segalanya. Event menambah satu lapisan tidak langsung, dan lapisan tidak langsung selalu memudarkan jejak alur saat dibaca. Dua kondisi berikut menandakan pemanggilan langsung lebih tepat: pertama, ketika pemanggil membutuhkan hasil dari operasi — event bersifat satu arah dan pengirimnya tidak menerima nilai balik; kedua, ketika alurnya sederhana dan pasti hanya punya satu reaksi, misalnya menghitung total keranjang lalu menampilkannya. Event mulai berbalik ketika satu aksi memiliki banyak efek independen dan kita ingin setiap efek berkembang sendiri.
Ada uji cepat yang bisa kita pakai saat ragu: tanyakan “berapa banyak pihak yang peduli kalau hal ini terjadi?”. Bila jawabannya satu dan kemungkinan besar akan tetap satu, pemanggilan langsung lebih jujur. Bila jawabannya bertambah seiring aplikasi tumbuh — pelanggan, tim gudang, analitik, sistem akuntansi — event menjaga aksi utama tetap ramping berapa pun jumlah pihak yang datang belakangan.
Event vs Panggilan Langsung #
| Aspek | Panggilan langsung | Event dan listener |
|---|---|---|
| Ketergantungan | pemanggil mengenal tiap target | pengirim hanya mengenal event |
| Menambah efek baru | sunting method pemanggil | daftarkan listener baru |
| Nilai balik | tersedia | tidak ada (satu arah) |
| Keterlambatan efek | terasa langsung di request | bisa dipindah ke queue |
| Keterbacaan alur | eksplisit, mudah dilacak | perlu event:list untuk melacak |
Dengan konsep ini di tangan, kita siap membangun komponen pertamanya secara nyata: pada chapter berikutnya kita membuat event class OrderPlaced dengan artisan, mengisinya dengan data pesanan, lalu mengirimkannya.