本卡屬 FR-090(母卡 CM-1682)第 6 棒。**前置:FR-091 18 支套件整理收口、CM-1689 接線收成 core/plugins/ 落地。**決策者 2026-09-12 裁排在 18 支後面。本卡定規則且動 60 多處引用,屬重量。
主專案的 DI container 現在替每支套件把它自己的 repo → domain service → app service 整條建一遍(asset 90 行、bulletin 98 行、auth 351 行),但套件的 build_services() 本來就會自組。這層重複是接一支套件要動「第四個檔」的原因。真正需要留在 DI 的只有「主專案其他模組直接用的套件 domain service」,例如 oscal 匯出要查資訊系統名稱。
首腦核對(2026-09-12):主專案其他 container 直接引用套件 container 的處數——asset 15 處(oscal/flow_control/associations/project/dashboard_apis 共 7 個 container)、system_core 14、upload_file 22、survey 11、notification 1、bulletin/iam/task_platform/issue/license 0。
主專案:/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/
di_containers/asset/asset_containers.py(90 行)
di_containers/bulletin/bulletin_containers.py(98 行)
di_containers/auth/auth_containers.py(351 行,iam)
di_containers/system_core/system_core_containers.py(96 行)
di_containers/upload_file/upload_file_containers.py(34 行)
di_containers/{notification,issue,license,survey,remote_agent}/
di_containers/containers.py(掛載與跨 container 傳遞)
core/plugins/<pkg>.py(CM-1689 產物)services= 那段
60 多處 Provide[<pkg>_container.xxx](開卡時 grep 列冊)
Provide[<pkg>_container.<domain_service>](現況,綁套件內部結構,套件改簽名主專案炸);(B) 套件在 plugin/contract.py 開一個 <Pkg>Services 的「宿主取用口」,主專案從 runtime() 或 handle.services 拿,不碰套件 domain 層。首腦傾向 B,但要 18 支整理完看實際有幾種用法再定。core/plugins/<pkg>.py 的 services= 不填讓套件自組,只留 adapter provider(若 adapter 有依賴主專案其他 service 才需 provider)。test_module_boundaries.py 加守衛:di_containers/** 不得 import jedi_*.infra.repository(主專案不該建套件的 repo)。突變:加回一行 → 紅。母卡:CM-1682 https://app.notion.com/p/FR-090-app-port-wrapper-3d9346da4cd081bb803bc6448ec3b3d0
裁示:主專案其他模組要用套件的東西,一律走套件在 plugin/contract.py 開的宿主取用口,不再 Provide[<pkg>_container.<domain_service>] 直綁套件 DI 內部。理由:A 案的耦合正是 FR-089 要解的病;B 是 D6 契約的延伸;時機上併進 CM-1559 那個套件版(1.1.0)不多開一版。