本卡為 FR-090 母案。FR-069/FR-080 把 route 與 service 上移進 jedi-* 套件後,主專案 app/ 下留了空殼、放錯層的 port 實作、轉發殼與 wrapper,同一件事有三四種做法。本案把主專案側統一成 jedi-asset 那一種形狀,四棒切分。子卡清單在末段。
後端模組化把功能一支支搬進套件,但搬完後主專案這邊沒有統一清場:有的目錄空了還留著、有的「套件需要主專案提供的東西」(port 實作)放在 app/ 而不是 infra/、有的用一層「轉發殼」包住套件、有的用 wrapper 在外面補欄位。同一種需求(例如把帳號換成暱稱顯示)在主專案有七處各自寫一遍。本案把這些收成一種做法,之後 18 支套件整理時才有乾淨的地基可對照。
決策者 2026-09-12 看到 app/auth/ 下九個檔問「不是已經拆成 iam 了嗎,怎麼還在」。首腦掃了主專案 app/ 全部 28 個目錄,發現這不是遺漏而是四種並存的做法:① 空殼目錄 8 個;② port 實作放 app/ 的 5 處(asset 是放 infra/);③ 轉發殼與 wrapper 6 檔;④ 補審計暱稱自己寫的 7 處(asset 已走 D8 identity port,主專案 app/ 零處用 IdentityContext)。決策者裁「全部做法都統一」,且先做主專案側再做 18 支套件。
infra/<pkg>/、接線放 core/<pkg>_wiring.py、套件上移後 app/<pkg>/ 整個刪、補暱稱由套件走 identity port 主專案不寫 wrapper、主專案 DI 不重複建套件 service 只給 port(除非主專案其他模組直接用該套件 domain service)。IdentityAdapters 沒有 identity 欄位、UserService/RoleService 不收 identity 參數。決策者已裁「開」。這是套件異動,第 3 棒做。created_user_name = ...。第 4 棒收。建議順序:第 1、2 棒立刻並行派(動的檔不重疊);第 3 棒等第 2 棒 commit 後派(第 2 棒會搬 app/auth/ 的兩個 port 實作,第 3 棒動 app/auth/ 其餘);第 4 棒可與第 3 棒並行。
pytest test/test_module_boundaries.py,第 2 棒起加一條守衛:app/ 下不得有實作 jedi_.domain.ports 或 jedi_.ports 介面的 class(AST 掃)。