組裝根 (Composition Root)

★★★★★Studied

FromDependency Injection

📚 From the Books

組裝根 (Composition Root)ConceptDI & IoC

整個應用程式只在一個地方把物件圖組裝起來——盡可能靠近進入點(mainStartup)。這個「唯一組裝處」就是 Composition Root:其餘程式碼只依賴抽象,永遠不自己 new 會變的依賴。它不是某個類別或框架功能,而是一個架構位置

🧠 When to Use

TIP

訊號:你想在測試或不同環境替換某個實作,卻發現「決定用哪個實作」的 new 散落在幾十個檔案裡——沒有一個集中的地方能一眼看出「這個系統是怎麼被組起來的」。

⚖️ Structure & Variants

最單純的形態 — main 就是天然的 Composition Root
fun main() {
    // Composition Root:所有 `new`、所有「選哪個實作」都集中在這裡
    val repository = SqlProductRepository(connectionString)
    val service    = ProductService(repository)
    val command    = UpdateCurrencyCommand(service)

    command.execute()   // 組裝完成,啟動整個流程
}
  • 所有具體型別的建立集中在最外層;ProductService 只認得 ProductRepository 介面。
  • 組裝好頂層物件後呼叫其方法,應用程式就跑起來了。
核心原則(書中反覆強調的四條)
  • 只有一個:整個應用程式應有且僅有一個 Composition Root。
  • 越靠近進入點越好Program.MainStartup、app 啟動事件。
  • 唯一允許直接建立依賴的地方:其餘程式碼只透過抽象互動。
  • 若用容器:Composition Root 是唯一可以參照容器的地方——在別處碰容器,就退化成 Service Locator 反模式
框架把物件搶去建時 —— 找『接縫 (Seam)』
  • 書中的關鍵洞察:物件組裝本身不難,難的是應用程式框架——框架常自己建立某些物件(Web 的 Controller、UI 的 Page),你無法直接用建構子注入。
  • 解法是找到框架提供的接縫:允許你插入自訂建立邏輯的擴充點(如 ASP.NET Core 的 IControllerActivator)。
  • 面對陌生框架先問:「它在哪裡建立我的物件?提供了什麼擴充點讓我介入?」找到接縫,就找到 Composition Root。

⚠️ Misuse & Anti-patterns

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading