跳轉到

Go Adapter Placement via Import Direction

概念概覽

核心判斷標準

核心知識

核心判斷標準

adapter 的正確位置由**它依賴誰**決定,而非由它服務誰決定:

  • 只依賴 infra/pkg(postgres, redis, luban, S3, config)→ 歸屬該 feature module 自己的目錄
  • 依賴其他 feature module → 歸屬 cmd/(composition root),因為只有這層擁有完整依賴圖

為什麼不能把跨模組 adapter 放進 module

  1. 製造 import cycle 風險(A module → B module → A module)
  2. 單元測試會拉進外部模組的所有依賴,降低隔離性
  3. 破壞 module 的自包含性

具體案例(MatchRPG_Server)

// 應搬進 internal/modules/dungeon/
dungeonGameDataAdapter     // 只依賴 luban infra
convertLubanDungeonConfig  // helper,同上
convertLubanConditions     // helper,同上

// 應搬進 internal/modules/sticker/
pendingStickerGameData     // 只依賴 infra

// 留在 cmd/twirpserver/(跨模組)
// 依賴多個 feature module 的 adapter

管理 cmd/ 膨脹的手法

按業務領域分檔:adapters_dungeon.goadapters_reward.go,讓 main.go 只保留 bootstrap 邏輯。

反例與遺留問題

sticker 模組現有跨模組 adapter 放在 module 內(歷史不一致),屬於技術債,需獨立 issue 清理,不在本次 main.go 瘦身範圍內

經驗教訓

  • import direction 比「放 cmd/ 還是 module/」更底層,是能在 import cycle 發生前就做出正確決策的思維框架

  • 跨模組 adapter 放進 module 的代價在測試時才會顯現(依賴爆炸),但在設計時就應排除

  • 歷史不一致的反例(sticker)不應阻止建立新規則,而是另立 issue 逐步對齊

常見陷阱

  • 以「adapter 服務哪個 module」而非「adapter 依賴哪些 module」決定位置,導致歸類錯誤

  • 為保持現有測試通過而不建立新規則,讓技術債持續累積

最佳實踐

  • 在 PR/設計文件中明文記錄原則:「只依賴 infra/shared pkg 的 adapter 屬於 module;依賴其他 feature module 的 adapter 屬於 composition layer」

  • cmd/ 內跨模組 adapter 用 adapters_.go 分檔,避免 main.go 膨脹

  • 新增 adapter 時先做 import dependency 分析,再決定位置

相關概念

  • clean-architecture----dangling---
  • composition-root----dangling---
  • import-cycle-prevention----dangling---
  • module-boundary----dangling---

來源 Sessions

日期 Session 貢獻摘要

| 2026-04-08 | c014ee9c-4558-46a6-9a87-1c574390eeec | 確立以 import direction 為唯一判斷標準的 adapter 定位原則,區分 module-owned vs composition-root adapter。 |


本概念頁面由 Semi-Brain Wiki 系統自動維護

最後更新: 2026-04-08