SuperAPI AI 工具集

code-organisation

编程与开发

查看项目

更新于 10-16 09:08

技能介绍

用途

该技能用于回答“代码应该放在哪里”以及“每个模块应该隐藏什么”。它把 Parnas 1972 年提出的信息隐藏原则作为唯一规则:每个模块拥有一个秘密——一个可能变化的设计决策——并把它藏在比秘密本身更小的接口之后。

模块是任何具有接口和实现的东西,与大小无关:一个应用切片(如 Nuxt layer 或 workspace package)、一个目录、一个文件、一个类、一个函数。规则在每个粒度上都相同,只是缩放不同。其收益是局部性:一次变更、一个 bug、一个测试,以及一个 agent 必须加载的上下文,都留在同一个模块内。

若要塑造单个模块的接口(深度、接缝、适配器),使用 /codebase-design。

主要能力

四项检查

对每个创建、移动或审计的模块运行这四项检查。全部通过才算完成。

  1. 单一秘密:用领域词汇完成“当 ___ 时它会变化”,不要提及技术。两个答案意味着两个模块;一个答案分散在多个模块中意味着它们是一个模块。
  2. 深:接口比它隐藏的东西更小。删除测试:删除该模块,如果其复杂性消失而不是在调用方重新出现,说明它只是一个透传,应内联。
  3. 封装:调用方和测试只能通过接口触及秘密。它们导入模块的入口点,而不是其背后的文件。接口携带领域类型,因此 ORM 行、SDK 对象和 HTTP 响应留在内部。没有调用方需要知道内部步骤的顺序。测试若必须越过接口,说明接缝位置错误。
  4. 有向:模块之间的依赖形成无环图,并指向更稳定的模块。耦合是平衡的:两个模块越近,它们可以互相知道得越多。同一模块内的文件可以共享内部;不同切片只共享窄接口。

缩放层级

应用:切片

按业务能力切分应用,每个切片一个限界上下文:auth、billing、email。每个切片拥有自己的 UI、逻辑、数据访问和测试。Nuxt layer、workspace package 和顶层目录是同一动作在不同工具成本下的体现。

一个切片发布一个入口点(layer exports、index.ts、package exports map),其他切片只使用它。一个切片拥有自己的表;其他切片通过其接口询问。

在切片内部,保持决策纯净,把 I/O 推到边缘:函数式核心,命令式外壳。核心无需 mock 即可测试。I/O 只在存在两个适配器时才放在端口和适配器之后;生产加一个测试替身也算。

同一个词在两个切片中可能含义不同(auth 和 billing 中的 Account)。给每个切片自己的模型,并在接口处翻译。

共享内核只保存每个切片都同意且很少变化的概念,如 money 和 ids。保持它极小。

目录:模块

先按功能分组,只在功能内部按技术角色分组:目录树应该喊出领域,而不是框架。一个切片拥有自己的 components/ 和 composables/(如 Nuxt layer)就是这条规则;一个顶层 components/ 容纳所有功能的组件则违反它。

目录模块的入口文件就是它的接口。其中其他所有文件都是私有的。

按知识拆分,而不是按执行顺序。fetch.ts、parse.ts 和 save.ts 如果都知道文件格式,那就是一个秘密分在三个文件里(时间分解)。

当两个切片长出相似代码时,重复直到第三次使用显示出真正的概念(三次法则)。错误的共享抽象会耦合切片;重复只花费打字成本。

文件、类、函数

当一个文件持有两个秘密时拆分;共享一个秘密的文件合并。大小是查看的理由,不是拆分的理由:一个长文件如果在一个小接口后只有一个秘密,那就是一个深模块。

一个类导出尽可能少的东西。每个字段一个 getter 和 setter 会公开其表示;给调用方他们需要的行为(告诉,不要询问)。

函数的参数就是它的接口。一个切换行为的布尔标志意味着两个函数。一个只透传给被调用方的参数会把被调用方的秘密向上泄漏。

文件内部的布局(报纸顺序、stepdown)属于 /clean-code。

命名

用领域的语言,按秘密命名模块:billing/、invoice-pdf/。需要“and”的名字标志着两个秘密。

放置新代码

  1. 命名它的秘密。完成“当 ___ 时它会变化”。当答案命名的是领域概念而非技术时完成。
  2. 找到拥有该秘密的模块。在切片、目录和文件缩放层级检查同级,而不仅仅是你所在的文件。当你有了归宿,或已证明不存在归宿时完成。
  3. 放置它,或创建一个以该秘密命名的同级模块,带其入口点。当其邻居与它在同一触发条件下变化时完成。
  4. 对你触及的每个模块运行四项检查。全部通过时完成。

强制边界

仅靠纪律维持的边界会侵蚀。当边界重要时,提出一个在违规时使 CI 失败的适应度函数:导入边界 lint 规则(eslint-plugin-boundaries、dependency-cruiser、import/no-cycle)、TypeScript 项目引用,或 package exports map。

坏味道

每个坏味道都是一项失败的检查。移动任何东西之前先命名所涉秘密。

坏味道 失败检查 修复
发散式变化:一个模块因不相关原因被编辑 单一秘密 按原因拆分
霰弹式修改:一次变更编辑许多模块 单一秘密 聚集到一个模块
层优先的顶层:controllers/、services/、models/ 单一秘密 按功能重新打包
通用桶:utils/、common/、helpers/、manager 单一秘密 把每项移到它服务的模块
特性依恋:代码主要使用另一个模块的数据 单一秘密 移到那个模块
时间分解 单一秘密 按共享知识合并
一个模型被含义不同的切片共享 单一秘密 每个切片一个模型,在接口处翻译
透传模块或包装器 深 内联它
类炎:许多小模块,每个接口很宽 深 合并为一个深模块
深导入:billing/internal/stripe 封装 导入入口点;若缺少东西则加宽它
接口中的基础设施类型 封装 在接缝处映射到领域类型
领域代码导入框架、数据库或 UI 有向 领域定义端口;基础设施适配它
导入循环 有向 把共享概念移到一个模块或内核,或合并

使用方式

该技能以规则和检查清单的形式提供,供在创建、移动或审计模块时逐项应用。它不提供安装命令或代码示例;具体执行依赖使用者将其原则应用到自己的代码库中。

适用场景

  • 决定新代码应放在哪个目录或模块。
  • 审计现有代码结构,识别坏味道并修复。
  • 设计应用切片、目录模块、文件、类或函数的边界。
  • 在 CI 中强制导入边界。

限制

资料未提供该技能的具体安装方式、运行环境、依赖工具或平台支持信息。它是一套设计原则与检查清单,而非可执行工具;实际效果取决于使用者对原则的理解与应用。