Đang tải...

Đăng ký tài khoản: Đừng để cái Email Welcome đánh sập Server của bạn!

05/05/2026
7 phút đọc
Đăng ký tài khoản: Đừng để cái Email Welcome đánh sập Server của bạn!
### Lời mở đầu: Tội ác của hàm Mail::send() Hãy nhìn vào một đoạn code Đăng ký kinh điển mà 90% anh em mới làm quen Laravel thường viết: ``` // ❌ BAD PRACTICE: G?...

Lời mở đầu: Tội ác của hàm Mail::send()

Hãy nhìn vào một đoạn code Đăng ký kinh điển mà 90% anh em mới làm quen Laravel thường viết:

// ❌ BAD PRACTICE: Gộp tất cả vào Controller
public function register(Request $request) 
{
 // 1. Tạo user
 $user = User::create([...]);
 
 // 2. Gửi email
 Mail::to($user->email)->send(new WelcomeEmail($user)); 
 
 // 3. Trả kết quả
 return response()->json(['message' => 'Thành công']);
}

Nhìn thì rất gọn, nhưng nó vi phạm nghiêm trọng về Hiệu năng. Hàm Mail::send() phải kết nối với SMTP Server (như Gmail, SendGrid, Mailgun) qua môi trường mạng (Network). Mạng thì luôn có độ trễ.

Khách hàng bấm "Đăng ký", màn hình loading quay đều 3 giây mới hiện thông báo thành công. Trải nghiệm cực kỳ tệ! Tồi tệ hơn, nếu server Mailgun lúc đó bị lỗi (Timeout), nguyên cái API đăng ký sẽ văng lỗi 500, dù data user thực ra đã được lưu vào DB rồi.

Hôm nay, chúng ta sẽ "giải phẫu" luồng này bằng kiến trúc Enterprise.

Bước 1: "Khiên chắn" Form Request

Đừng bao giờ validate dữ liệu trực tiếp trong Controller. Hãy đẩy nó ra FormRequest để Controller luôn sạch sẽ.

php artisan make:request RegisterUserRequest
// app/Http/Requests/RegisterUserRequest.php
namespace AppHttpRequests;

use IlluminateFoundationHttpFormRequest;

class RegisterUserRequest extends FormRequest
{
 public function authorize(): bool
 {
 return true;
 }

 public function rules(): array
 {
 return [
 'name' => 'required|string|max:255',
 'email' => 'required|string|email|max:255|unique:users',
 'password' => 'required|string|min:8|confirmed',
 ];
 }
}

Bước 2: Action Pattern và DB Transaction

Thay vì dùng Service ôm đồm nhiều thứ (như UserService chứa cả login, register, update), ta dùng Action Pattern (Mẫu thiết kế Hành động) để đảm bảo nguyên lý Single Responsibility (Đơn trách nhiệm). Mỗi class chỉ làm đúng 1 việc.

Đồng thời, khi đăng ký, ta thường phải tạo thêm profile rỗng, hoặc phân quyền rỗng. Hãy bọc nó trong DB::transaction để đảm bảo: Nếu lỗi ở đâu, rollback lại toàn bộ, không có chuyện user sinh ra mà mất profile.

# Laravel chưa có sẵn lệnh make:action, bạn tự tạo thư mục nhé
mkdir app/Actions
// app/Actions/RegisterUserAction.php
namespace AppActions;

use AppModelsUser;
use AppModelsUserProfile;
use IlluminateSupportFacadesDB;
use IlluminateSupportFacadesHash;

class RegisterUserAction
{
 public function execute(array $data): User
 {
 return DB::transaction(function () use ($data) {
 // 1. Tạo core User
 $user = User::create([
 'name' => $data['name'],
 'email' => $data['email'],
 'password' => Hash::make($data['password']),
 ]);

 // 2. Tạo Profile đi kèm
 UserProfile::create([
 'user_id' => $user->id,
 'avatar' => 'default.png',
 ]);

 // 3. (Quan trọng) KHÔNG gửi email ở đây!
 
 return $user;
 });
 }
 }

Bước 3: Đánh thức hệ thống bằng Event & Queue (Bất đồng bộ)

Đây là "vũ khí hạng nặng" giúp API của bạn nhanh như chớp. Sau khi tạo user xong, hệ thống chỉ cần hét lên: "Ê, có một thằng mới đăng ký nè!" (Phát ra Event). Rồi nó kết thúc API luôn.

Việc gửi email sẽ do một thằng "Culi" đứng đằng sau (Queue Worker) âm thầm nhặt cái Event đó lên và đi gửi.

1. Tạo Event và Listener

php artisan make:event UserRegistered
php artisan make:listener SendWelcomeEmail --event=UserRegistered

2. File Event (Mang theo data)

// app/Events/UserRegistered.php
namespace AppEvents;

use AppModelsUser;
use IlluminateFoundationEventsDispatchable;
use IlluminateQueueSerializesModels;

class UserRegistered
{
 use Dispatchable, SerializesModels;

 public User $user;

 public function __construct(User $user)
 {
 $this->user = $user;
 }
}

3. File Listener (Implement ShouldQueue để chạy ngầm)

Đây là mấu chốt, chỉ cần implements ShouldQueue, Laravel sẽ tự ném task này vào Redis hoặc RabbitMQ thay vì chạy đồng bộ.

// app/Listeners/SendWelcomeEmail.php
namespace AppListeners;

use AppEventsUserRegistered;
use IlluminateContractsQueueShouldQueue; // BẮT BUỘC PHẢI CÓ CÁI NÀY
use IlluminateSupportFacadesMail;
use AppMailWelcomeEmail;

class SendWelcomeEmail implements ShouldQueue
{
 public function handle(UserRegistered $event): void
 {
 // Thằng Culi (Worker) sẽ từ từ chạy đoạn này ở background
 Mail::to($event->user->email)->send(new WelcomeEmail($event->user));
 }
}

(Nhớ đăng ký cặp Event-Listener này trong EventServiceProvider nhé).

Bước 4: Controller siêu mỏng (Thin Controller)

Nhìn lại cái Controller của chúng ta đi. Nó giờ chỉ đóng vai trò người điều phối, sạch sẽ đến bất ngờ:

// app/Http/Controllers/Api/AuthController.php
namespace AppHttpControllersApi;

use AppHttpControllersController;
use AppHttpRequestsRegisterUserRequest;
use AppActionsRegisterUserAction;
use AppEventsUserRegistered;

class AuthController extends Controller
{
 public function register(RegisterUserRequest $request, RegisterUserAction $action)
 {
 // 1. Dữ liệu đã được validate sạch sẽ từ Request
 
 // 2. Gọi Action thực thi logic lõi
 $user = $action->execute($request->validated());

 // 3. Bắn Event ra cho hệ thống biết (Hệ thống Queue sẽ tự bắt lấy)
 UserRegistered::dispatch($user);

 // 4. Trả response ngay lập tức (Chỉ mất 50ms)
 return response()->json([
 'success' => true,
 'message' => 'Đăng ký thành công! Vui lòng kiểm tra email.',
 'data' => $user
 ], 201);
 }
}

Bước 5: Thử lửa với Postman

Để chứng minh kiến trúc này "đỉnh" thế nào, hãy mở Postman lên.

(Lưu ý: Mở terminal chạy lệnh php artisan queue:work để bật thằng Culi xử lý hàng đợi lên nhé).

1. Setup Request Đăng ký:

  • Method: POST
  • URL: [http://127.0.0.1:8000/api/register](http://127.0.0.1:8000/api/register)
  • Headers: Accept: application/json
  • Body (raw - JSON):
{
 "name": "Kỹ sư dởm",
 "email": "kysudom@example.com",
 "password": "Password123!",
 "password_confirmation": "Password123!"
}

2. Kết quả:

  • Response: Bắn về ngay lập tức mã 201 Created cùng data user.
  • Nhìn vào Terminal Postman (Response Time): Bạn sẽ thấy con số thời gian phản hồi chỉ loanh quanh 40ms - 80ms.
  • Nhìn vào Terminal chạy Queue của Laravel: Tầm 1-2 giây sau, bạn mới thấy dòng chữ Processing: AppListenersSendWelcomeEmailProcessed hiện lên.

Đó chính là sự kỳ diệu của Bất đồng bộ (Asynchronous). Người dùng không phải chờ cái Email được gửi xong. Hệ thống của bạn đã sẵn sàng hứng hàng ngàn request đăng ký mỗi giây mà không sợ tắc nghẽn luồng xử lý mạng.

Tóm lại

Một tính năng cơ bản như Đăng ký tài khoản, nếu áp dụng đúng tư duy hệ thống:

  1. FormRequest: Bảo vệ cửa ngõ.
  2. Action + Transaction: Bảo vệ toàn vẹn dữ liệu Database.
  3. Event + Queue: Giải phóng thời gian chờ, tối ưu UX và System Performance.

Đây mới là cách code mà các tập đoàn lớn (Enterprise) kỳ vọng ở một kỹ sư Backend. Hãy lôi dự án cũ ra và refactor lại cái Controller đăng ký của anh em ngay đi!

📚 Nguồn: Viblo

Bình luận

0 bình luận

Email không hiển thị công khai.

Chưa có bình luận nào. Hãy là người đầu tiên bình luận.

Chia sẻ bài viết

Cần tư vấn?

Liên hệ với chúng tôi để được hỗ trợ

Liên hệ ngay

Bài viết liên quan

Session 1 - Securing Accounts: Bảo vệ tài khoản trong thế giới số
19/06/2026

Session 1 - Securing Accounts: Bảo vệ tài khoản trong thế giới số

## Mục tiêu của Session Session đầu tiên tập trung vào một chủ đề rất quen thuộc nhưng cũng là mục tiêu tấn công phổ biến nhất hiện nay: **tài khoản người...

Đọc thêm
Tổng hợp kênh hỗ trợ FPT dành cho khách hàng cá nhân
19/06/2026

Tổng hợp kênh hỗ trợ FPT dành cho khách hàng cá nhân

Khi sử dụng Internet, truyền hình hoặc camera, khách hàng cá nhân đôi khi cần hỗ trợ về lắp đặt, báo lỗi, thanh toán, hợp đồng hoặc nâng cấp dịch vụ. Thay...

Đọc thêm
Lắp mạng cho sinh viên: Cách chọn gói rẻ nhưng vẫn khỏe
19/06/2026

Lắp mạng cho sinh viên: Cách chọn gói rẻ nhưng vẫn khỏe

Với sinh viên, Internet không chỉ để giải trí. Một đường truyền ổn định giúp học online, nộp bài, gọi video nhóm, xem tài liệu, làm thêm từ xa và thư giãn s...

Đọc thêm

Bắt đầu dự án của bạn

Hãy để Flash Dev đồng hành cùng bạn

Liên hệ ngay