---
title: Dùng Dependency Injection thay vì khởi tạo trực tiếp
impact: HIGH
impactDescription: DI qua protocol giúp dễ dàng mock trong unit test, tách biệt các tầng và tránh coupling cứng giữa các class.
tags: swift, ios, dependency-injection, testing, architecture, protocol
---

## Dùng Dependency Injection thay vì khởi tạo trực tiếp

Thay vì khởi tạo dependency trực tiếp bên trong class, hãy inject chúng qua `init` sử dụng protocol. Điều này đặc biệt quan trọng trong iOS khi viết unit test cho ViewModel, Interactor, hay Service.

**Incorrect (khởi tạo trực tiếp):**

```swift
class OrderViewModel {
    // Coupling cứng - không thể mock khi test
    private let apiService = OrderAPIService()
    private let database = CoreDataManager()
    private let analytics = FirebaseAnalytics()

    func loadOrders() {
        apiService.fetchOrders { [weak self] result in
            // xử lý
        }
    }
}
```

**Correct (inject qua protocol):**

```swift
protocol OrderAPIServiceProtocol {
    func fetchOrders(completion: @escaping (Result<[Order], Error>) -> Void)
}

protocol AnalyticsProtocol {
    func track(event: String)
}

class OrderViewModel {
    private let apiService: OrderAPIServiceProtocol
    private let analytics: AnalyticsProtocol

    init(
        apiService: OrderAPIServiceProtocol = OrderAPIService(),
        analytics: AnalyticsProtocol = FirebaseAnalytics()
    ) {
        self.apiService = apiService
        self.analytics = analytics
    }

    func loadOrders() {
        apiService.fetchOrders { [weak self] result in
            // xử lý
        }
    }
}

// Unit test dễ dàng
class MockOrderAPIService: OrderAPIServiceProtocol {
    func fetchOrders(completion: @escaping (Result<[Order], Error>) -> Void) {
        completion(.success([Order(id: "1", total: 100)]))
    }
}
let vm = OrderViewModel(apiService: MockOrderAPIService(), analytics: MockAnalytics())
```

**Tools:** Code Review, Needle/Swinject (DI Framework)

