Chế độ xem

Tin theo khu vực

Thị trường
Chứng khoánKinh doanhCông nghệXem tất cả →

Dữ liệu chỉ mang tính tham khảo · Cập nhật theo phiên

Trang chủ/Lập trình/Design Pattern Laravel
PHPLaravelDesign PatternPhỏng vấn

Design Pattern trong Laravel & PHP: Giải Thích Dễ Hiểu Để Đi Làm & Phỏng Vấn

Design Pattern không phải là thứ gì xa lạ — chúng là những giải pháp đã được kiểm chứng để giải quyết các bài toán lập trình lặp đi lặp lại. Laravel áp dụng hầu hết các pattern này xuyên suốt framework. Hiểu chúng sẽ giúp bạn viết code tốt hơn, debug nhanh hơn, và tự tin hơn khi phỏng vấn.

12 phút đọc·Cập nhật 18/05/2026
Trước khi bắt đầu: Design Pattern chia làm 3 nhóm chính — Creational (tạo object: Factory, Singleton), Structural (cấu trúc: Facade, Decorator), Behavioral (hành vi: Observer, Strategy). Bài này tập trung vào các pattern Laravel dùng nhiều nhất trong thực tế.

1. Dependency Injection (DI)

01

Dependency Injection

Creational — Truyền phụ thuộc từ bên ngoài vào class

Một câu để nhớ: Thay vì class tự đi mua nguyên liệu, ai đó sẽ giao nguyên liệu tới tận nơi cho class.

❌ Không dùng DI — BAD
class OrderService {
    public function placeOrder($data) {
        $payment = new StripePayment(); // Phụ thuộc cứng
        $mailer  = new Mailer();        // Phụ thuộc cứng

        $payment->charge($data['amount']);
        $mailer->send($data['email'], 'Đơn hàng đã đặt!');
    }
}

Vấn đề: muốn test OrderService thì phải kéo theo cả StripePaymentMailer thật — không mock được.

✅ Dùng DI — GOOD
class OrderService {
    public function __construct(
        private PaymentInterface $payment,
        private MailerInterface  $mailer,
    ) {}

    public function placeOrder($data) {
        $this->payment->charge($data['amount']);
        $this->mailer->send($data['email'], 'Đơn hàng đã đặt!');
    }
}

// Laravel tự inject khi resolve
$service = app(OrderService::class);
  • Dễ test: chỉ cần mock interface, không cần class thật
  • Đổi payment từ Stripe → PayPal? Chỉ đổi 1 dòng binding
  • Tuân thủ nguyên tắc SOLID (D — Dependency Inversion)
  • Code rõ ràng hơn: dependencies được khai báo tường minh

Câu hỏi phỏng vấn thường gặp

  • Q1.Dependency Injection là gì? Tại sao dùng?
  • Q2.Sự khác nhau giữa Constructor Injection và Method Injection?
  • Q3.DI giúp ích gì cho việc viết Unit Test?

2. Service Container

02

Service Container

Laravel IoC Container — Tự động inject dependencies

Một câu để nhớ: Service Container là kho đăng ký + nhà máy tự động — bạn đăng ký "khi cần X thì tạo Y", Laravel tự làm phần còn lại.

Binding trong AppServiceProvider
// app/Providers/AppServiceProvider.php
public function register(): void {
    // Bind interface → implementation
    $this->app->bind(
        PaymentInterface::class,
        StripePayment::class
    );

    // Singleton: chỉ tạo 1 lần duy nhất
    $this->app->singleton(
        CacheManager::class,
        fn($app) => new CacheManager($app['config']['cache'])
    );

    // Instance cố định
    $this->app->instance(
        'api.key',
        env('STRIPE_SECRET')
    );
}
Laravel tự động resolve
// Trong Controller — Laravel inject tự động
class OrderController extends Controller {
    public function __construct(
        private PaymentInterface $payment  // → StripePayment
    ) {}
}

// Resolve thủ công khi cần
$payment = app(PaymentInterface::class);
$payment = resolve(PaymentInterface::class);

// Resolve với tham số
$report = app(ReportService::class, ['format' => 'pdf']);
Mẹo nhớ: bind() = tạo mới mỗi lần gọi. singleton() = tạo 1 lần, tái sử dụng. instance() = dùng object đã có sẵn.
  • Giải quyết dependency tự động — không cần new thủ công
  • Đổi implementation chỉ cần sửa 1 dòng binding
  • Quản lý vòng đời object (singleton vs transient)
  • Nền tảng cho toàn bộ Laravel: Route, Middleware, Queue...

Câu hỏi phỏng vấn thường gặp

  • Q1.Service Container khác gì Service Provider?
  • Q2.Khi nào dùng bind() vs singleton()?
  • Q3.Giải thích IoC (Inversion of Control) là gì?

3. Repository Pattern

03

Repository Pattern

Structural — Tách biệt logic truy vấn database

Một câu để nhớ: Repository là thư viện tra cứu — Controller hỏi "cho tôi user id=5", Repository biết cách lấy dữ liệu từ đâu (DB, cache, API...).

Cấu trúc Repository Pattern
// 1. Interface — định nghĩa hợp đồng
interface UserRepositoryInterface {
    public function find(int $id): ?User;
    public function all(): Collection;
    public function create(array $data): User;
    public function update(int $id, array $data): bool;
    public function delete(int $id): bool;
    public function findByEmail(string $email): ?User;
}

// 2. Implementation — thực thi với Eloquent
class UserRepository implements UserRepositoryInterface {
    public function find(int $id): ?User {
        return User::find($id);
    }
    public function all(): Collection {
        return User::orderBy('created_at', 'desc')->get();
    }
    public function create(array $data): User {
        return User::create($data);
    }
    public function findByEmail(string $email): ?User {
        return User::where('email', $email)->first();
    }
    // ...
}

// 3. Binding — đăng ký vào container
// AppServiceProvider.php
$this->app->bind(
    UserRepositoryInterface::class,
    UserRepository::class
);

// 4. Sử dụng trong Controller
class UserController extends Controller {
    public function __construct(
        private UserRepositoryInterface $users
    ) {}

    public function show(int $id) {
        $user = $this->users->find($id);
        return $user
            ? response()->json($user)
            : response()->json(['error' => 'Not found'], 404);
    }
}
Đừng over-engineer! Nếu app nhỏ hoặc chắc chắn không đổi database, dùng Eloquent thẳng trong Service cũng được. Repository hữu ích khi: cần mock cho test, cần đổi data source, hoặc query logic phức tạp.
  • Controller sạch — không chứa query logic
  • Dễ viết Unit Test (mock repository thay vì DB thật)
  • Đổi từ MySQL → MongoDB? Chỉ viết thêm MongoUserRepository
  • Tái sử dụng query logic ở nhiều nơi
  • Tuân thủ Single Responsibility Principle

Câu hỏi phỏng vấn thường gặp

  • Q1.Repository Pattern là gì? Lợi ích so với dùng Eloquent trực tiếp?
  • Q2.Tại sao nên bind Interface thay vì Implementation?
  • Q3.Repository Pattern có nhược điểm gì không?
  • Q4.Sự khác biệt giữa Repository và Service?

4. Singleton Pattern

04

Singleton Pattern

Creational — Đảm bảo chỉ có 1 instance duy nhất

Một câu để nhớ: Singleton như tổng giám đốc công ty — chỉ có 1 người, ai cũng gặp người đó, không bao giờ có 2.

Singleton thuần PHP
class DatabaseConnection {
    private static ?self $instance = null;
    private PDO $pdo;

    // Constructor private — không ai new được từ ngoài
    private function __construct() {
        $this->pdo = new PDO(
            'mysql:host=localhost;dbname=myapp',
            'root', 'secret'
        );
    }

    // Clone disabled
    private function __clone() {}

    public static function getInstance(): static {
        if (static::$instance === null) {
            static::$instance = new static();
        }
        return static::$instance;
    }

    public function query(string $sql): array {
        return $this->pdo->query($sql)->fetchAll();
    }
}

// Sử dụng
$db1 = DatabaseConnection::getInstance();
$db2 = DatabaseConnection::getInstance();
var_dump($db1 === $db2); // true — cùng 1 object
Singleton trong Laravel (dùng singleton binding)
// Cách Laravel làm — KHUYẾN NGHỊ hơn PHP thuần
// AppServiceProvider.php
$this->app->singleton(Logger::class, function ($app) {
    return new Logger(
        storage_path('logs/app.log'),
        $app['config']['app.log_level']
    );
});

// Mọi nơi trong app đều nhận cùng 1 instance
class OrderService {
    public function __construct(private Logger $logger) {}

    public function placeOrder($data) {
        $this->logger->info('Order placed', $data);
    }
}

class PaymentService {
    public function __construct(private Logger $logger) {}
    // Đây cũng là Logger instance đó — không tạo mới
}
Laravel way: Ưu tiên dùng singleton() trong Service Container thay vì tự implement static Singleton. Code testable hơn, tránh global state.
  • Tiết kiệm tài nguyên: không tạo kết nối DB mới mỗi request
  • Đảm bảo consistency: mọi nơi dùng cùng 1 config/logger
  • Dễ quản lý shared state (cache, queue connection...)

Câu hỏi phỏng vấn thường gặp

  • Q1.Singleton Pattern là gì? Khi nào nên dùng?
  • Q2.Nhược điểm của Singleton là gì? (global state, khó test)
  • Q3.Làm sao test code có Singleton? (override binding trong test)
  • Q4.Singleton vs Static class — khác nhau thế nào?

5. Factory Pattern

05

Factory Pattern

Creational — Tạo object mà không cần biết class cụ thể

Một câu để nhớ: Factory như nhà máy — bạn đặt hàng "tôi cần Payment", nhà máy tự quyết định dây chuyền nào sản xuất.

Simple Factory — cơ bản nhất
interface NotificationInterface {
    public function send(string $to, string $message): bool;
}

class EmailNotification implements NotificationInterface {
    public function send(string $to, string $message): bool {
        // Gửi email...
        return true;
    }
}

class SmsNotification implements NotificationInterface {
    public function send(string $to, string $message): bool {
        // Gửi SMS...
        return true;
    }
}

class PushNotification implements NotificationInterface {
    public function send(string $to, string $message): bool {
        // Push notification...
        return true;
    }
}

// Factory
class NotificationFactory {
    public static function create(string $channel): NotificationInterface {
        return match($channel) {
            'email' => new EmailNotification(),
            'sms'   => new SmsNotification(),
            'push'  => new PushNotification(),
            default => throw new InvalidArgumentException(
                "Unknown channel: {$channel}"
            ),
        };
    }
}

// Sử dụng
$channel = $user->preferred_channel; // 'email' | 'sms' | 'push'
$notifier = NotificationFactory::create($channel);
$notifier->send($user->contact, 'Đơn hàng đã xác nhận!');
Factory trong Laravel (Model Factory cho Testing)
// database/factories/UserFactory.php
class UserFactory extends Factory {
    public function definition(): array {
        return [
            'name'     => fake()->name(),
            'email'    => fake()->unique()->safeEmail(),
            'password' => Hash::make('password'),
            'role'     => 'user',
        ];
    }

    // States — biến thể của factory
    public function admin(): static {
        return $this->state(['role' => 'admin']);
    }

    public function suspended(): static {
        return $this->state(['suspended_at' => now()]);
    }
}

// Dùng trong Test
$user  = User::factory()->create();          // 1 user bình thường
$admin = User::factory()->admin()->create(); // 1 admin
$users = User::factory()->count(50)->create(); // 50 users
  • Tách biệt logic tạo object khỏi logic nghiệp vụ
  • Dễ thêm loại mới — chỉ thêm class + 1 case trong factory
  • Client code không phụ thuộc vào concrete class
  • Model Factory giúp tạo test data nhanh, nhất quán

Câu hỏi phỏng vấn thường gặp

  • Q1.Factory Pattern là gì? Khác Simple Factory vs Factory Method vs Abstract Factory?
  • Q2.Khi nào nên dùng Factory thay vì new trực tiếp?
  • Q3.Laravel Model Factory dùng pattern gì?

6. Observer Pattern

06

Observer Pattern

Behavioral — Lắng nghe và phản ứng với sự kiện

Một câu để nhớ: Observer như đăng ký nhận thông báo — khi có sự kiện xảy ra (user đăng ký), tất cả "người đăng ký" đều được thông báo tự động.

Observer trong Laravel
// 1. Tạo Observer
// php artisan make:observer UserObserver --model=User

class UserObserver {
    // Chạy SAU khi tạo user mới
    public function created(User $user): void {
        // Gửi email chào mừng
        Mail::to($user->email)->send(new WelcomeMail($user));

        // Tạo profile mặc định
        $user->profile()->create([
            'avatar' => 'default.png',
            'bio'    => '',
        ]);

        // Log sự kiện
        Log::info("New user registered: {$user->email}");
    }

    // Chạy TRƯỚC khi update
    public function updating(User $user): void {
        if ($user->isDirty('email')) {
            $user->email_verified_at = null; // Reset verification
        }
    }

    // Chạy SAU khi xoá
    public function deleted(User $user): void {
        // Dọn dẹp dữ liệu liên quan
        Storage::deleteDirectory("users/{$user->id}");
    }
}

// 2. Đăng ký Observer — AppServiceProvider hoặc Model
// Cách 1: trong boot() của AppServiceProvider
User::observe(UserObserver::class);

// Cách 2: dùng attribute (Laravel 10+)
#[ObservedBy(UserObserver::class)]
class User extends Model { ... }
So sánh: có Observer vs không có Observer
// ❌ Không dùng Observer — Controller bị phình to
class UserController {
    public function store(Request $request) {
        $user = User::create($request->validated());

        // Những thứ này không liên quan đến controller
        Mail::to($user->email)->send(new WelcomeMail($user));
        $user->profile()->create(['avatar' => 'default.png']);
        Log::info("New user: {$user->email}");
        Cache::tags('users')->flush();

        return response()->json($user, 201);
    }
}

// ✅ Dùng Observer — Controller sạch sẽ
class UserController {
    public function store(Request $request) {
        $user = User::create($request->validated());
        // Observer tự động xử lý email, profile, log, cache...
        return response()->json($user, 201);
    }
}
  • Controller/Service sạch — không chứa side effects
  • Tự động kích hoạt khi model thay đổi — không cần nhớ gọi
  • Dễ thêm hành vi mới mà không đụng code cũ
  • Tuân thủ Open/Closed Principle

Câu hỏi phỏng vấn thường gặp

  • Q1.Observer Pattern là gì? Ví dụ thực tế trong Laravel?
  • Q2.Observer khác gì Events/Listeners trong Laravel?
  • Q3.Nhược điểm của Observer? (logic ẩn, khó debug)
  • Q4.Khi nào dùng Observer, khi nào dùng Event/Listener?

7. Tổng kết — Khi Nào Dùng Cái Nào?

PatternLoạiDùng khi
Dependency InjectionCreationalLuôn dùng — nền tảng của clean code
Service ContainerCreationalCần quản lý dependency tự động trong app
RepositoryStructuralApp có nhiều query phức tạp, cần test DB
SingletonCreationalConfig, Logger, DB Connection — shared resource
FactoryCreationalCần tạo nhiều loại object theo điều kiện
ObserverBehavioralCần thực hiện side-effect khi model thay đổi
Lời khuyên cho phỏng vấn: Đừng chỉ học thuộc định nghĩa. Người phỏng vấn thích nghe bạn giải thích bằng ví dụ thực tế từ dự án của mình. Hãy chuẩn bị câu trả lời theo dạng: "Trong dự án X, tôi dùng pattern Y để giải quyết vấn đề Z, lợi ích là..."

📚 Thứ tự học đề xuất

  1. 1Dependency Injection — hiểu khái niệm core
  2. 2Service Container — cách Laravel implement DI
  3. 3Repository Pattern — áp dụng vào project thật
  4. 4Factory + Singleton — khi gặp use case cụ thể
  5. 5Observer — refactor side-effects trong project

Góc nhìn của bạn

Trang này hữu ích với bạn không?

hoặc
♥ VNEWS
Menu điều hướng
Đã lưu
Thông báo
Đang xem tin trong nước