© 2026 Laravel

SOLID - Dependency Injection (DI) từ cơ bản đến Architect + áp dụng Laravel

4 phút đọc 47 lượt xem

#Dependency Injection (DI)

“Đừng tự tạo dependency bên trong class – hãy nhận nó từ bên ngoài”

DI là kỹ thuật triển khai của DIP. Nếu DIP là nguyên lý thiết kế, thì DI là cách hiện thực hoá nguyên lý đó trong code.

#1. Bản chất của DI

#1.1 Inversion ở mức object graph

Không phải class tự new dependency:

class A {
    private B $b;
    public function __construct() {
        $this->b = new B();
    }
}

Mà dependency được cung cấp từ bên ngoài:

class A {
    public function __construct(private B $b) {}
}

👉 Quyền kiểm soát việc tạo object được chuyển ra ngoài (composition root)

#1.2 Object graph

Một hệ thống thực tế là một đồ thị object:

Controller → Service → Repository → DB

DI giúp:

  • Build graph này ở 1 nơi duy nhất
  • Quản lý lifecycle
  • Thay đổi implementation mà không đổi business logic

#2. Các loại Dependency Injection

#2.1 Constructor Injection

class UserService {
    public function __construct(
        private UserRepository $repo
    ) {}
}

Ưu điểm:

  • Immutable dependency
  • Rõ ràng
  • Dễ test

#2.2 Method Injection

class ReportService {
    public function generate(Logger $logger) {}
}

Use case: dependency optional hoặc chỉ dùng 1 lần

#2.3 Property Injection (không khuyến khích)

class A {
    public B $b;
}

Vấn đề:

  • Không đảm bảo initialized
  • Khó trace

#3. Composition Root (khái niệm cực quan trọng)

Nơi duy nhất khởi tạo và wiring toàn bộ dependency

#3.1 Sai lầm phổ biến

  • new object rải rác khắp code

#3.2 Đúng

function bootstrap(): App {
    $db = new MySQLConnection();
    $repo = new UserRepository($db);
    $service = new UserService($repo);

    return new App($service);
}

#4. DI Container

#4.1 Vai trò

  • Resolve dependency
  • Manage lifecycle
  • Auto wiring

#4.2 Tự build container đơn giản

class Container {
    private array $bindings = [];

    public function bind(string $abstract, callable $factory) {
        $this->bindings[$abstract] = $factory;
    }

    public function make(string $abstract) {
        return $this->bindings[$abstract]($this);
    }
}

#5. Laravel DI Container (thực chiến)

#5.1 Auto resolve

class UserService {
    public function __construct(UserRepository $repo) {}
}

Laravel tự resolve nếu class có thể new được

#5.2 Bind interface

$this->app->bind(
    UserRepository::class,
    EloquentUserRepository::class
);

#5.3 Singleton vs Bind

$this->app->singleton(Logger::class, function () {
    return new Logger();
});
Type Behavior
bind new instance mỗi lần
singleton shared instance

#5.4 Contextual binding

$this->app->when(AdminService::class)
    ->needs(Logger::class)
    ->give(AdminLogger::class);

#6. Lifecycle & Scope

#6.1 Singleton

  • DB connection
  • Logger

#6.2 Transient

  • Service nhẹ

#6.3 Scoped (request)

  • HTTP request lifecycle

👉 Sai scope = bug khó debug

#7. Anti-patterns

#7.1 Service Locator

app()->make(UserService::class);

👉 Hidden dependency

#7.2 Over-injection

Constructor 10+ dependencies → smell

#7.3 God container

  • Bind mọi thứ
  • Khó maintain

#8. DI + Testing

#8.1 Mock dễ dàng

$service = new UserService(
    new FakeUserRepository()
);

#8.2 Isolation

  • Không cần DB thật
  • Test nhanh

#9. DI trong kiến trúc lớn

#9.1 Clean Architecture

  • Outer layer inject vào inner

#9.2 Hexagonal

  • Adapter inject vào Port

#9.3 Microservices

  • Inject client (HTTP, gRPC)

#10. Performance & Trade-off

#Ưu

  • Flexible
  • Testable

#Nhược

  • Indirection
  • Debug khó hơn

#11. Khi nào KHÔNG dùng DI

  • Script nhỏ
  • Không có dependency

#12. Best Practices

  • Constructor injection first
  • Interface khi cần
  • Keep constructor nhỏ
  • Group dependency (Facade/Service)

#13. Interview Questions

DI là gì?

Summary:

  • Inject dependency từ ngoài

Deep:

  • Control object creation
DI khác DIP?

Summary:

  • DI là technique

Deep:

  • DIP là principle
Khi nào dùng singleton?

Summary:

  • Shared resource

Deep:

  • Tránh tạo nhiều instance
Service Locator là gì?

Summary:

  • Lấy dependency từ container

Deep:

  • Hidden dependency → anti-pattern

#14. Kết luận

DI là nền tảng để:

  • Viết code testable
  • Build architecture scalable

👉 Junior: biết dùng DI 👉 Senior: hiểu lifecycle + container 👉 Architect: thiết kế object graph + boundary