𝗠𝗮𝘀𝘁𝗲𝗿𝗶𝗻𝗴 𝗘𝘃𝗲𝗻𝘁-𝗗𝗿𝗶𝘃𝗲𝗻 𝗗𝗲𝗰𝗼𝘂𝗽𝗹𝗶𝗻𝗴 𝗶𝗻 𝗟𝗮𝗿𝗮𝘃𝗲𝗹
Controller Laravel anda sering menjadi tempat pengumpulan logik perniagaan.
Anda bermula dengan aliran pendaftaran yang ringkas. Tidak lama kemudian, anda menambah pemberitahuan e-mel, amaran Slack, log audit, dan panggilan API ke dalam satu kaedah tunggal. Ini mewujudkan fat controller.
Fat controller menjadikan kod anda rapuh. Ia sukar untuk diuji. Ia melanggar Single Responsibility Principle.
Anda tidak memerlukan alatan kompleks seperti RabbitMQ untuk membaiki perkara ini. Laravel mempunyai sistem acara (event system) terbina dalam yang berfungsi untuk kebanyakan keperluan.
Masalah dengan tight coupling: Jika API buletin perlahan, pendaftaran pengguna anda turut menjadi perlahan. Jika perkhidmatan e-mel gagal, keseluruhan permintaan (request) akan gagal.
Penyelesaiannya: Event-Driven Architecture.
Event bertindak sebagai lapisan tengah. Controller anda mengumumkan sesuatu tindakan. Listener akan bertindak balas terhadap tindakan tersebut secara bebas.
Controller yang ramping (lean) kelihatan seperti ini:
public function register(RegisterRequest $request)
{
$user = User::create($request->validated());
UserRegistered::dispatch($user);
return response()->json(['message' => 'Success'], 201);
}
Controller kini hanya mengendalikan pengekalan data (data persistence). Ia tidak mempedulikan kesan sampingan (side effects).
Anda mendapat tiga manfaat utama:
- Prestasi: Pengguna mendapat respons dengan serta-merta. Tugasan berat dijalankan di latar belakang menggunakan interface
ShouldQueue. - Ketahanan (Resilience): Jika sesuatu perkhidmatan tergendala, listener boleh mencuba semula tugasan tersebut tanpa menjejaskan aplikasi utama.
- Kebolehluasan (Extensibility): Anda boleh menambah ciri baharu, seperti push notifications, dengan menambah listener baharu. Anda tidak perlu menyentuh controller asal.
Amalan terbaik untuk diikuti:
- Fokus pada kesan sampingan: Gunakan event untuk pemprosesan pasca (post-processing). Jangan gunakannya untuk logik teras yang mesti berlaku serta-merta.
- Gunakan nama yang deskriptif: Gunakan nama dalam bentuk masa lampau (past-tense) seperti
OrderPlacedatauUserRegistered. Ini menunjukkan tindakan tersebut telah pun berlaku. - Elakkan abstraksi berlebihan: Jika sebahagian kod itu ringkas dan hanya digunakan di satu tempat, panggilan fungsi adalah lebih baik daripada event.
Gunakan Eloquent Observers untuk perubahan pangkalan data. Gunakan Events untuk tindakan perniagaan.
Refactoring kepada event adalah tentang ketahanan (durability). Ia menjadikan kod anda lebih mudah untuk dinyahpepijat (debug) dan lebih pantas untuk diuji.
Pilih satu kesan sampingan yang mengganggu dalam controller anda dan pindahkannya ke listener hari ini.
Cuba contoh sandbox ini: https://onlinephp.io/c/1f7b2
Melampaui Fat Controllers: Menguasai Penyahkaitan Berasaskan Acara dalam Laravel
Pernahkah anda mendapati Controller anda semakin lama semakin besar, kompleks, dan sukar untuk diuji? Jika ya, anda sedang menghadapi masalah "Fat Controller".
Dalam pembangunan aplikasi Laravel, adalah mudah untuk terjerumus ke dalam tabiti memasukkan terlalu banyak logik ke dalam Controller. Walaupun ia mungkin berfungsi pada mulanya, lama-kelamaan ia akan menjadi mimpi ngeri penyelenggaraan.
Masalah: Controller yang Terlalu Sarat (Fat Controllers)
Bayangkan anda mempunyai fungsi pendaftaran pengguna. Dalam pendekatan "Fat Controller", kod anda mungkin kelihatan seperti ini:
public function store(Request $request)
{
// 1. Validasi data
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8',
]);
// 2. Simpan pengguna ke pangkalan data
$user = User::create($validated);
// 3. Berikan bonus pendaftaran (Logik perniagaan)
$user->assignRole('customer');
$user->bonusPoints += 100;
$user->save();
// 4. Hantar e-mel aluan
Mail::to($user->email)->send(new WelcomeMail($user));
// 5. Log aktiviti
Log::info("Pengguna baharu didaftarkan: {$user->email}");
return response()->json($user, 201);
}
Mengapa ini bermasalah?
- Melanggar Prinsip Tanggungjawab Tunggal (Single Responsibility Principle - SRP): Controller ini bertanggungjawab untuk validasi, penyimpanan data, logik perniagaan, penghantaran e-mel, dan log sistem.
- Sukar Diuji: Untuk menguji fungsi pendaftaran, anda juga terpaksa menguji penghantaran e-mel dan logik bonus.
- Sukar Diselenggara: Jika anda ingin menukar cara e-mel dihantar, anda perlu mengubah Controller ini, yang meningkatkan risiko merosakkan logik pendaftaran.
Penyelesaian: Penyahkaitan Berasaskan Acara (Event-Driven Decoupling)
Daripada melakukan semua perkara di atas dalam satu fungsi, kita boleh menggunakan Events dan Listeners dalam Laravel untuk mengasingkan (decouple) tugas-tugas tersebut.
Dengan pendekatan ini, Controller hanya perlu melakukan satu perkara: mencetuskan (dispatch) satu acara yang menyatakan bahawa "Pengguna telah didaftarkan".
Langkah 1: Mencipta Acara (Event)
Pertama, kita cipta acara UserRegistered:
php artisan make:event UserRegistered
Langkah 2: Mencipta Pendengar (Listener)
Seterusnya, kita cipta beberapa pendengar untuk mengendalikan tugas-tugas yang berbeza secara berasingan:
php artisan make:listener SendWelcomeEmail --event=UserRegistered
php artisan make:listener AssignCustomerRole --event=UserRegistered
php artisan make:listener GrantRegistrationBonus --event=UserRegistered
Langkah 3: Mengemas Kini Controller
Sekarang, Controller kita menjadi jauh lebih bersih, ringkas, dan fokus kepada tugas utamanya:
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8',
]);
$user = User::create($validated);
// Cetuskan acara!
event(new UserRegistered($user));
return response()->json($user, 201);
}
Mengapa Ini Lebih Baik?
- Penyahkaitan (Decoupling): Controller tidak lagi tahu tentang e-mel, bonus, atau peranan pengguna. Ia hanya tahu bahawa seorang pengguna telah didaftarkan.
- Prinsip Tanggungjawab Tunggal (SRP): Setiap Listener kini mempunyai satu tanggungjawab sahaja.
SendWelcomeEmailhanya menguruskan e-mel, manakalaGrantRegistrationBonushanya menguruskan bonus. - Kebolehskalaan (Scalability): Jika anda ingin menambah tugas baharu (contohnya: menghantar data ke sistem pemasaran pihak ketiga), anda hanya perlu mencipta Listener baharu tanpa perlu menyentuh kod dalam Controller asal.
- Kebolehujian (Testability): Anda boleh menguji setiap Listener secara berasingan dengan mudah tanpa perlu mensimulasikan keseluruhan proses pendaftaran.
Kesimpulan
Menguasai penggunaan Events dan Listeners adalah langkah penting untuk beralih daripada kod yang berselerak kepada seni bina yang bersih, modular, dan profesional dalam Laravel. Dengan mengurangkan beban Controller, anda membina aplikasi yang lebih mudah untuk berkembang dan diselenggara dalam jangka masa panjang.