OpenAPI 文档起步时都是干净的单文件,但到四十个操作就变成 2000 行,到上百个操作后每个 PR 都在合并时冲突,没人愿意花一下午重建上下文。常见的建议是「用 $ref 拆开」,但怎么拆、按什么边界拆、拆完工具链会出什么问题,往往没人讲清楚。这套布局和规则能撑住 200 个以上操作的 API。
结构上先按产物类型分,再按业务域分。paths 和 schemas 变更频率不同、维护者不同,不共用目录。入口文件 openapi.yaml 只负责组装,不负责描述,保留 info、servers、security、tags,其余全部通过 $ref 指向 paths/、schemas/、responses/、parameters/、examples/、callbacks/ 等子目录。工具只需找到入口文件,一切从它解析,不靠扫描目录。
$ref 约定有五条:每个产物只有一个权威定义;引用组件对象而非内联片段;paths 引用 components,components 不反向引用 paths;紧耦合的私有类型优先同文件引用;不要引用未固定版本的远程仓库,共享 schema 应 vendored 进仓库。发布产物通常需要打包成单文件,供不跟随相对引用的文档渲染器和网关使用;生成的 SDK 尽量保留 ref,让调用方拿到具名类型。
常见坑包括跨文件循环引用、3.0 中 $ref 忽略兄弟字段、把 allOf 当导入系统用、示例与 schema 漂移、误提交生成目录。CI 门禁建议覆盖:OpenAPI 3.2 结构校验、无外网访问的引用解析、风格 lint、示例对 schema 校验、与已发布版本的破坏性变更 diff,以及打包产物的再次校验。
操作数在 30 个以下时,单文件更好搜索、工具零配置,不必拆。出现多个常规编辑者、代码所有权边界或规范合并冲突时再拆。接手单体规范时增量迁移:先把 components.schemas 拆成文件,再按域拆 paths,每次移动后校验打包结果。
