Bỏ qua để đến nội dung

Dependency Injection quy mô lớn

Code lớn dần, dependency tree phình to. Chương này bàn về chiến lược DI khi app có nhiều module và hàng trăm class.

Cách phổ biến: gom tất cả dependency vào một object khổng lồ:

struct AppContext {
let api: API
let user: User
let store: Store
let analytics: Analytics
// ... 50+ properties
}

Vấn đề:

  • God Object: Mọi class đều phụ thuộc vào AppContext.
  • Khó biết class cần gì: Nhìn init(context: AppContext) — class này dùng api hay store hay cả hai?
  • Global state trá hình: Thực chất không khác Singleton.

Thay vì một tree khổng lồ, hãy chia thành sub-trees và mỗi phần tự quản lý:

Phần Settings có mục Payment. Payment cần APIStore:

class Settings {
private let user: User
private let store: Store
private let api: API
func setupMonetization() -> Monetization {
let paymentFactory = PaymentSettingsFactory(api: api)
return Monetization(user: user, store: store, factory: paymentFactory)
}
}

Settings đóng vai trò composition root cục bộ — quản lý dependency cho sub-tree của nó.

Trong app lớn, không thể khởi tạo tất cả dependency tại Main. Thay vào đó, có nhiều composition root rải rác:

  • Main: Khởi tạo core dependency (API, Store, Auth).
  • TabBar: Khởi tạo các feature chính (Home, Search, Settings).
  • Settings: Khởi tạo các sub-features (Payment, Profile, Notifications).

Mỗi điểm là một Factory hoặc Container cục bộ.

Khi tách module, cần quan tâm đến public interface giữa các module:

import FeatureCourse
import CoreNetworking
// App là "người nối dây" giữa các module
let network = ProductionNetwork() // CoreNetworking
let api = API(network: network) // CoreNetworking
let courseAPI = CourseAPI(api: api) // FeatureCourse

Vấn đề: CourseAPI (feature module) yêu cầu API (networking module) → feature phụ thuộc vào networking.

Giải pháp:

  • Dùng Protocol (interface) đặt trong feature module → dependency inversion.
  • Hoặc chấp nhận dependency nếu module đó là shared utilities cho toàn app.

Nguyên tắc: Giữ public interface giữa các module nhỏ gọn và rõ ràng.


Kết luận: Ở quy mô lớn: tránh God Container, chia nhỏ dependency tree thành sub-trees, tạo nhiều composition root cục bộ. Khi chia module, dùng Protocol để invert dependency.