Files
ShadowsocksX-NG/docs/adr/0029-source-layout-declares-layer-and-theme.md

28 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 源码目录即分层与主题,边界由守卫断言
Status: Accepted (2026-10-05)
`App/` 按分层组织(Composition / Application / Presentation / UI / PlatformEffects / Resources),`Domain/` 按主题组织,主题名取自 `GLOSSARY.md` 的章节划分(配置与订阅、代理规则、代理控制与网络、设置、凭据与诊断),避免同一概念在文档与目录里有两个名字。两个目录的根层只放子目录、不放平铺文件——目录本身就是架构声明。
边界由 `Tests/SkeletonTests.swift` 的守卫断言,且一律**正面圈定**而非禁令加豁免登记:视图层的守卫只扫 `App/UI/`(不得引用运行时控制器、工作流实现协作者、原始目录/凭据存储类型),`Domain/` 的守卫只断言根下无平铺 `.swift`、树下只有 `.swift`。把文件放进 `Application/`、`PlatformEffects/` 或某个主题目录,即自动落在扫描范围之外,无需登记豁免;新增主题目录也不需要改断言。
各目录的划分与各自职责由 [工程结构文档](../project-structure.zh.md)维护——本 ADR 只固定组织原则,不维护清单。
## Considered options
- **平铺 + 禁令表 + 豁免登记**:守卫默认扫描整个目录,再逐个登记要放行的例外。每加一个运行时适配器都要改守卫,豁免清单本身成了需要维护的知识,而「这个文件属于哪一层/哪个主题」仍然无处声明。
- **feature-first(按功能纵向切分)**:把某个功能的领域类型、工作流与视图放进同一个目录。与既有分层冲突——工作流依赖的运行时控制器与目录存储会变成视图的同层邻居,边界退化成不可断言的约定。
- **`Domain/` 也照搬分层**:领域内部不存在 `App/` 那样的层间依赖方向(没有「领域视图」),照搬只会得到一个叫 Services 的杂物箱。
- **让守卫锁定主题目录集合**:即正向列举「允许的目录」。未采纳——断言应当固定「不留平铺文件」这条不变量,而不是把主题划分冻结成一份需要同步维护的清单;新增主题目录是正常演进。
## Consequences
XcodeGen 带来两条**在代码里不可见**的约束:
- 源路径的 `excludes` glob **不跨 `/`**:裸文件名只匹配被扫描目录的根层。Core 测试镜像因此必须用 `**/MainApp.swift` 这类递归形式;文件下移后旧写法会**静默**失效,把 `@main` 组合根编进测试镜像。
- `App/`、`Domain/` 整目录是 source path,其中任何非源码文件都会被 XcodeGen 收进 Resources 阶段并封进签名后的 app bundle。因此文档不放 `App/`(`project.yml` 排除 `**/*.md`),`Domain/` 则由守卫禁止出现非 `.swift` 文件。
跨进程契约按**文件**引用而非按目录:wrapper 与 system proxy helper 两个 target 各自显式列出所需的 `Domain/` 文件,因为它们只该拿到跨进程契约,而不是整个领域层。代价是目录内移动文件必须同步 `project.yml`;漏改时 xcodegen 以 `missing source directory` 直接失败,不静默。
在目录内移动文件因此是低代价操作:模块名、类型名与 import 均不变,`task format` 与 lint 基线不变,版本历史可继续追溯。这条约定分散记录在 `project.yml` 的分层注释、[工程结构文档](../project-structure.zh.md)与本 ADR;新增主题目录时只需同步前两处(`DomainLayoutTests` 失败信息里列举的目录名会过期,断言本身不会失败)。