mcpField
F
fitness-functions

fitness-functions

github.com/event-catalog测试与质量149 次安装2 天前更新
代码仓库

关于 fitness-functions

EventCatalog Fitness Functions 是一项技能,可将架构规则定义为 TypeScript 检查,并针对 EventCatalog 目录数据运行。团队可查看违规项,并用命令行检查的非零退出码拦截拉取请求;检查对象是目录文档,而非运行中的系统。

主要功能

  1. 01TypeScript 架构规则
  2. 02只读目录 SDK 查询
  3. 03含资源链接的违规报告
  4. 04错误与警告级别
  5. 05用于拉取请求门禁的命令行检查

适用场景

  1. 01执行领域所有权边界规则
  2. 02检查目录资源间的依赖
  3. 03标记缺失的所有者或消息通道
  4. 04在 CI 中拦截目录变更

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/event-catalog/skills --skill fitness-functions

安装后在 Claude Code 中输入/fitness-functions确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
ci.md
examples.md

详细介绍

EventCatalog 适应度函数

适应度函数是用代码编写的架构规则。每个函数都是一个 TypeScript 文件,通过只读 SDK 读取目录,并返回找到的违规项。eventcatalog fitness check 会运行所有函数、打印结果、写入 JSON 报告,并在规则未通过时以非零状态退出,因此可以用这些规则来卡住拉取请求。

团队决定健康的架构应该是什么样;EventCatalog 运行规则并报告结果。

适应度函数检查的是目录记录的架构,而不是正在运行的系统。规则的效果取决于它读取的目录数据。

要求(请先检查)

  1. 版本 — @eventcatalog/core 5.4.0 或更高版本(检查目录的 package.json)。或者,@eventcatalog/cli 1.4.0 或更高版本无需 core 即可运行相同的检查(npx @eventcatalog/cli fitness check)。Node.js 20 或更高版本。
  2. 编辑器类型(可选) — 无需安装其他内容即可运行适应度函数,但要使用类型和自动补全,请将 @eventcatalog/fitness 安装为开发依赖项。使用 create-eventcatalog 创建的目录已包含该依赖项,以及 fitness/ 中的两个入门函数和一个 npm run fitness 脚本。

适应度函数、代码检查器还是治理?

编写代码前,先选择合适的工具:

规则涉及……使用
单个文件是否有效:frontmatter 字段、必需属性、无效引用、文件布局@eventcatalog/linter
拉取请求中发生了什么变化(新增使用者、架构变更、弃用),以及通知eventcatalog governance check(governance.yaml)
架构的形态:所有权、命名、边界、依赖关系、谁可以访问什么,涉及多个资源适应度函数(本指南)

说明

步骤 1:找到目录并检查现有内容

找到目录(包含 eventcatalog.config.js 的文件夹),并检查:

  • 是否存在根目录下的 fitness/ 文件夹,或资源文件夹内的 fitness/ 文件夹?阅读现有函数——复用其中的辅助函数并遵循其风格,不要创建重复规则。
  • 适应度函数的 id 在整个目录中必须唯一。列出已在使用的 id。
  • package.json 是否包含 fitness 脚本?是否已安装 @eventcatalog/fitness?
  • .eventcatalog/fitness/ 是否位于 .gitignore 中?检查会将报告写入该目录;报告不应提交。
步骤 2:将规则转化为明确的检查

像“服务应拥有自己的数据”这样的规则过于含糊,无法编写代码。请明确具体要求;如有不清楚之处,请询问用户:

  • 具体什么情况算违规?(“某个服务向另一个领域系统所属的数据存储写入数据。”)
  • **适用于哪些资源?**全部资源、一个领域、一个系统,还是一个服务?
  • **仅检查最新版本,还是检查所有版本?**几乎总是只检查最新版本({ latestOnly: true })。
  • 严重性 — error 会导致检查(和 CI)失败;warning 会报告问题,但不会导致失败。如果新规则会在现有目录中发现大量违规项,请先将其设为 warning,之后再收紧要求。
  • **每条违规应指向什么,以及如何修复?**每条违规都应列出涉及的资源并建议修复方法。

检查目录数据是否支持该规则:如果规则需要 writesTo、owners 或 sends.to,而目录没有记录这些信息,请说明这一点——否则该规则会因缺少数据而始终通过。

第 3 步:选择文件存放位置

文件所在位置决定它接收的 resource,而不是它能查看的内容:每个函数都可以查询整个目录。

规则适用于将文件放在resource 是
整个目录fitness/<name>.fitness.ts(允许使用子文件夹)undefined
一个领域domains/Orders/fitness/<name>.fitness.tsOrders 领域
一个系统…/systems/checkout-system/fitness/<name>.fitness.ts该系统
一个服务…/services/OrderService/fitness/<name>.fitness.ts该服务
一个事件、命令或查询…/events/OrderCreated/fitness/<name>.fitness.ts该消息
一个数据存储…/containers/payment-database/fitness/<name>.fitness.ts该数据存储(type: 'container')
一个特定版本…/versioned/1.0.0/fitness/<name>.fitness.ts该版本

频道、流程、实体、数据产品、ADR 和图表的处理方式相同。规则如下:

  • 只有以 .fitness.ts 结尾的文件才会运行。其他 .ts 文件(共享辅助文件)不会运行,因此 fitness 函数可以导入它们。
  • fitness/ 文件夹必须直接位于资源文件夹内(与其 index.mdx 并列)。其他位置的 .fitness.ts 文件不会运行,并会被报告为诊断信息。
  • 联合目录(federated/…)中的 fitness 函数不会运行,联合也不会复制 fitness/ 文件夹。
第 4 步:编写 fitness 函数
// domains/Orders/fitness/no-shared-data-stores.fitness.ts
import { defineFitnessFunction } from '@eventcatalog/fitness';

export default defineFitnessFunction({
  id: 'orders-no-shared-data-stores', // unique across the catalog
  name: 'Orders services only use Orders data stores',
  description: 'Reaching into another domain’s database couples the domains together.',
  severity: 'error', // or 'warning'; defaults to 'error'

  async run({ resource, catalog }) {
    if (resource?.type !== 'domain') throw new Error('Expected to run beside a domain');

    // ...read what you need from `catalog`, decide what's wrong...

    return [
      {
        message: 'Order Service writes to payment-database, which is outside the Orders domain',
        resources: [
          { type: 'service', id: 'order-service', version: '1.0.0' },
          { type: 'container', id: 'payment-database' },
        ],
        details: 'Optional extra context',
        suggestion: 'Get payment data through the Payments API or its events.',
      },
    ]; // [] means the rule passed
  },
});

编写时请遵循以下约定:

  • export default defineFitnessFunction({ ... }) —— 每个文件定义一个函数。run 可以是同步或异步的。
  • 返回违规项,不要抛出异常。 空数组表示通过。抛出异常(或返回任何不是 { message, resources?, details?, suggestion? } 数组的值)都会使函数状态变为 error,这也会导致检查失败,但表示“无法评估”,而不是“架构有误”。
  • 根据 type 缩小 resource 的类型范围(resource?.type === 'domain' 会得到类型化的 Domain);如果文件位置不正确,请抛出明确的错误。
  • 使用 catalog 的读取方法 —— 名称和参数与 @eventcatalog/sdk 相同:getDomains、getServices、getEvents、getDataStores、getTeams、getGraph、getOwnersForResource、getSchemaForMessage、getResourcesByProperty,……集合获取方法在没有内容时会返回 undefined,因此请这样写:(await catalog.getEvents({ latestOnly: true })) ?? []。这里没有写入方法,而且每次调用都会返回副本,因此修改结果不会造成影响。完整列表见:references/api.md。
  • 只读取一次集合并构建查找结构(映射或集合),不要在循环中调用 catalog —— 例如,一次性收集每个服务发送的消息,而不是针对每条消息调用 getProducersAndConsumersForMessage。
  • 在每个违规项中引用资源,格式为 { type, id, version? } —— 在 EventCatalog 中,这些资源会从违规项链接出去。类型包括:domain、system、service、agent、event、command、query、flow、channel、entity、container(也接受 dataStore)、data-product、adr、diagram、team、user。
  • 编写读者可以据此采取行动的消息:指出资源名称(Order Service sends Order Amended without a channel),并在 suggestion 中写明修复方法。
  • 将共享逻辑放在辅助文件中(例如 fitness/helpers/catalog.ts),并使用相对路径导入。使用 import type { ReadOnlyCatalog } from '@eventcatalog/fitness' 导入类型。
  • 保持函数的确定性:不要发起网络调用、写入文件,也不要依赖当前日期。

常见规则的示例(所有权、命名、生产者、循环依赖、领域边界、扇出、频道、架构、数据访问):references/examples.md。

步骤 5:运行并验证
npx eventcatalog fitness check          # or: npm run fitness
npx eventcatalog fitness check --format json

输出会列出每个函数及其结果,然后列出违规项和错误:

EventCatalog Fitness

✓ Every message has an owner                        Passed
✗ orders / Orders services only use Orders data…    1 violation
    • Order Service writes to payment-database, which is outside the Orders domain
      Resources: order-service (service), payment-database (container)
      Suggestion: Get payment data through the Payments API or its events.
! Every event has a producer                        3 warnings

3 fitness functions evaluated

交付前请验证——永远不会失败的规则还不如没有规则:

  1. 新函数已运行,且状态符合预期。如果显示 Error,请修复(参见故障排除)。
  2. 结果正确。 手动对照目录文件,检查几个违规项(以及几个通过项)。
  3. 它确实可能失败。 如果它在当前目录中通过,请确认它能捕获真实的违规情况——与用户一起推演,或者在目录的临时副本中破坏一项资源,然后在那里运行检查。绝不要将测试修改留在用户的目录中。
  4. 引用均可解析。 显示为 (…, not found in the catalog) 的引用,其类型或 ID 有误。

退出代码:0 全部通过或仅有警告,1 某个函数失败或无法评估,2 检查本身无法运行。

步骤 6:在 CI 中运行

在检查目录变更的流水线中,安装依赖后运行 npx eventcatalog fitness check。当规则失败时,它会以 1 退出,从而使作业失败。GitHub Actions、GitLab 和发布报告的示例:references/ci.md。

故障排除

症状原因 / 修复方法
完全没有列出函数文件名不以 .fitness.ts 结尾、不在 fitness/ 文件夹中,或者位于 federated/、node_modules、dist 或点号开头的文件夹下
! <file>: Not run: fitness functions must be in a fitness/ folder将文件移至根目录下的 fitness/ 文件夹,或直接移至某个资源的文件夹中
No resource found for this fitness function: '<folder>' has no index.mdx or index.mdfitness/ 文件夹不是直接位于某个资源的文件夹中
… is not in a resource folder (such as domains/ or services/)包含 fitness/ 的文件夹不是已识别的资源文件夹
The file has no default export添加 export default defineFitnessFunction({ ... })
Invalid fitness function: \id` must be a non-empty string, …`缺少 id/name/run,或者 severity 不是 'error' 或 'warning'
Another fitness function already has the id '<id>'ID 必须唯一;按路径排序时,第一个文件会运行,后续文件会报错
run() must return an array of violations … / Violation 0: …run 返回了其他内容,或者某项违规缺少 message,或包含无效的 resources 条目
Cannot find module './helpers/…'辅助文件的相对路径有误——数一数到目录根目录需要向上几层
资源后出现 (…, not found in the catalog)引用的 type 或 id 与任何资源都不匹配;该引用会保留,但不会建立链接
仅在编辑器中出现 Cannot find module '@eventcatalog/fitness'将 @eventcatalog/fitness 安装为开发依赖;检查本身无需它也能运行
fitness is not a command@eventcatalog/core 版本低于 5.4.0——请升级,或使用 npx @eventcatalog/cli fitness check

信任

Fitness 函数是来自目录的代码,运行时拥有与项目中其他脚本相同的访问权限。无论在本地还是 CI 中,都只能运行你信任的 Fitness 函数。EventCatalog 绝不会在浏览器中、页面请求期间或从联合目录中运行它们。

参考文件

  • references/api.md — defineFitnessFunction、上下文、每个 catalog 方法、违规项、状态、JSON 报告、CLI 选项和发现规则
  • references/examples.md — 常见规则的完整 Fitness 函数示例,包括全局和限定范围的规则,以及共享辅助函数
  • references/ci.md — 在 GitHub Actions 和 GitLab 中运行检查、发布报告、读取 JSON 输出

评论

支持 Markdown