Archgate CLI:将架构决策文档转化为自动化检查规则
1. 项目概述从文档到执行的架构治理革命在软件开发的漫长周期里我们总会遇到一个经典难题架构决策文档ADR写完了然后呢它们通常被静静地存放在docs/decisions/目录下随着时间推移逐渐被团队遗忘。新加入的成员可能完全不知道这些决策的存在老成员在赶进度时也可能无意中违反。最终精心设计的架构蓝图与实际代码渐行渐远技术债悄然累积。Archgate CLI 的出现正是为了解决这个“最后一公里”的问题——它让架构决策不再仅仅是文档而是变成了可执行、可验证、可自动化的治理规则。简单来说Archgate 是一个命令行工具它允许你将用 Markdown 写的 ADR 文件与用 TypeScript 写的自动化检查规则.rules.ts文件配对。当你运行archgate check时它会扫描你的代码库执行这些规则并报告任何违反既定架构决策的地方。你可以把它集成到 CI/CD 流水线或 Git 预提交钩子中确保每一次代码提交都符合架构规范。更酷的是它还能通过插件让 Claude Code、Cursor 这类 AI 编程助手在写代码前就“阅读”并理解这些 ADR实现“架构感知”的智能编码。这不仅仅是又一个静态代码分析工具。它建立了一种轻量级但强制的反馈循环决策文档 - 规则代码 - 执行检查 - 反馈报告。对于追求工程卓越、希望将架构治理真正落地的团队来说这是一个游戏规则的改变者。无论你是 Tech Lead 希望固化团队的最佳实践还是架构师需要确保跨团队的一致性Archgate 都提供了一个极其优雅的解决方案。2. 核心设计思路双层架构与自动化闭环Archgate 的设计哲学非常清晰“Write an ADR once. Enforce it everywhere.”一次编写处处执行。为了实现这个目标它采用了清晰的双层架构设计并在其上构建了自动化与智能化的闭环。2.1 双层架构文档与规则的分离与结合第一层是“ADRs as documents”。这继承了传统的 ADR 实践使用 Markdown 文件通常包含 YAML 前置元数据来记录决策。文件存放在项目根目录下的.archgate/adrs/文件夹中。一个典型的 ADR 会包含决策的上下文、考虑的选项、最终决定的原因以及由此产生的约束该做什么不该做什么。这部分是给人读的确保了决策背景和意图的可追溯性。第二层是“ADRs as rules”。这是 Archgate 的灵魂。每个 ADR Markdown 文件都可以有一个同名的伴侣文件后缀为.rules.ts。这个 TypeScript 文件导出一个或多个规则函数这些函数使用 Archgate 提供的 API 来检查代码库。例如一个关于“所有错误必须使用特定类封装”的 ADR其对应的.rules.ts文件就会包含扫描代码、寻找throw new Error(...)或console.error等模式并标记违规的规则。这种分离是精妙的。文档层保持了可读性和历史记录而规则层则提供了机器可读、可执行的逻辑。两者通过文件名关联如ARCH-001-command-structure.md和ARCH-001-command-structure.rules.ts形成了一个完整的决策单元。2.2 自动化闭环从本地开发到持续集成有了规则执行就成了关键。Archgate CLI 提供了check命令来运行所有规则。其工作流程如下CLI 定位.archgate/adrs/目录。找到所有.rules.ts文件。加载并执行这些规则通常使用 Bun 运行时因其启动速度快。规则函数遍历指定的源代码文件如src/**/*.ts。当发现违反规则的代码模式时规则会报告一个“违规”包含文件名、行号、列号和具体的错误信息。CLI 汇总所有违规并输出到终端。如果发现任何违规进程将以退出码 1 结束。这个退出码机制是集成自动化的基石。你可以轻松地将其加入你的工作流Git Hooks (预提交/pre-commit)在pre-commit钩子中运行archgate check开发者可以在提交前立即得到反馈防止不合规的代码进入仓库。CI/CD Pipeline (如 GitHub Actions, GitLab CI)在拉取请求PR的 CI 任务中运行archgate check。如果检查失败退出码为1CI 任务标记为失败从而阻止合并。这为架构规范提供了强制性的守门员。注意将archgate check加入 CI 时务必确保安装步骤正确。如果项目将 archgate 作为 devDependency 安装CI 脚本中应使用npx archgate check或通过npm run脚本调用。2.3 智能化延伸赋能AI编程助手这是 Archgate 最具前瞻性的部分。通过为 Claude Code 和 Cursor 等编辑器插件Archgate 能将 ADR 上下文直接注入 AI 编程助手的“大脑”。其工作模式可能是上下文提供当你在编辑器中请求 AI 生成或修改代码时插件会自动将相关领域的 ADR 文档作为背景信息发送给 AI 模型。实时验证AI 生成的代码草稿在落地前可能先经过本地规则集的快速校验。决策捕获在与 AI 协作过程中如果发现了一种新的、值得记录的架构模式你可以通过命令快速生成一个新的 ADR 草稿。这形成了一个更高级的闭环人类制定决策 - 规则自动化检查 - AI 在决策约束下辅助编码 - 新决策被捕获。它极大地降低了遵循架构规范的心智负担尤其对于快速迭代的团队和新手开发者。3. 从零开始安装、初始化与第一个规则理论说得再多不如亲手实践。让我们一步步搭建一个 Archgate 环境并创建我们的第一个可执行 ADR。3.1 环境准备与安装Archgate 的安装非常灵活你可以根据团队习惯选择。方案一独立安装推荐无 Node.js 依赖对于希望工具链简洁或需要在 CI 环境中避免全局 Node 依赖的项目这是最佳选择。它下载一个预编译的二进制文件。# macOS 或 Linux curl -fsSL https://cli.archgate.dev/install-unix | sh这个脚本会自动检测你的系统架构下载合适的二进制文件到~/.archgate/bin/并可能提示你将其加入PATH环境变量。方案二通过 Node.js 包管理器安装如果你的团队已经深度使用 npm/yarn/pnpm/bun这种方式更统一。# 使用 npm 全局安装 npm install -g archgate # 或作为项目开发依赖安装更推荐便于版本锁定 npm install -D archgate作为开发依赖安装后你可以在package.json的scripts中定义命令如archgate-check: archgate check然后使用npm run archgate-check来运行。在 CI 中安装项目依赖后即可直接使用npx archgate check。安装完成后在终端运行archgate --version验证是否成功。3.2 项目初始化与目录结构进入你的项目根目录执行初始化命令cd /path/to/your/project archgate init这个命令会做两件事在项目根目录下创建.archgate/文件夹及其子目录。在.archgate/adrs/目录下生成一个示例 ADR 文件例如ARCH-001-example-decision.md和对应的ARCH-001-example-decision.rules.ts。让我们看一下生成的结构.archgate/ └── adrs/ ├── ARCH-001-example-decision.md └── ARCH-001-example-decision.rules.ts.archgate/目录应该被加入.gitignore吗通常不应该。这个目录包含的是团队共享的架构契约它应该被纳入版本控制以便所有开发者都能获取相同的规则。你可以把.archgate/看作是和package.json、tsconfig.json同等级别的项目配置元数据。3.3 编写你的第一个 ADR 文档打开生成的ARCH-001-example-decision.md你会看到一个模板。一个完整的 ADR 通常包含以下部分基于轻量级 ADR 格式--- id: ARCH-001 status: accepted date: 2023-10-27 deciders: [alice, bob] --- # 1. 统一使用应用层错误类 ## 上下文与问题陈述 目前项目中错误抛出方式杂乱有的直接 throw new Error(msg)有的用 throw error string还有自定义错误对象但结构不统一。这导致前端错误监控难以归类错误信息缺乏结构化数据如错误码、用户提示也使得错误处理逻辑重复且不一致。 ## 决策驱动因素 * **可维护性**统一的错误类便于识别和定位错误来源。 * **可观测性**结构化错误信息能更好地与监控系统如Sentry集成。 * **用户体验**能够区分面向用户的安全提示和内部技术错误。 * **团队协作**减少在错误处理方式上的分歧和重复劳动。 ## 考虑过的方案 1. 继续现状直接使用 Error。 2. 引入一个自定义的 AppError 基类包含 code、message、userMessage、originalError 等属性。 3. 使用第三方错误处理库如 verror, boom。 ## 决策结果 我们选择方案2创建并强制使用一个内部的 AppError 类。 **所有业务逻辑和API层抛出的错误都必须是 AppError 或其子类的实例。** 原生的 Error 或字符串仅允许在底层库代码或非常特殊的场景下使用且需要明确的注释说明。 ## 影响与后果 ### 积极影响 * 错误处理逻辑标准化。 * 错误信息更丰富便于调试和监控。 * 前端可以基于 error.code 显示友好的用户提示。 ### 需要付出的成本 * 需要重构现有代码中不符合规范的错误抛出。 * 新开发者需要学习并遵守此规范。这个文档清晰地记录了决策的来龙去脉是团队的知识资产。3.4 编写配套的自动化规则文档写好了但如何确保大家遵守这就需要打开伴侣文件ARCH-001-example-decision.rules.ts。Archgate 提供了一个基于 TypeScript 的规则 API。让我们编写一个规则来强制执行上述 ADR。// .archgate/adrs/ARCH-001-example-decision.rules.ts import { defineRule, RuleContext } from archgate/cli; export default defineRule({ // 规则元数据会显示在报告里 meta: { id: no-native-error, description: 禁止在业务代码中直接抛出原生 Error 或字符串, }, // 规则实现 async check(ctx: RuleContext) { const { glob, createViolation } ctx; // 1. 指定要检查的文件范围 const files await glob([src/**/*.ts, src/**/*.js, src/**/*.tsx, src/**/*.jsx]); for (const file of files) { const content await file.text(); // 2. 将文件内容按行分割便于定位 const lines content.split(\n); // 3. 使用简单的正则匹配实际项目中可能需要更复杂的AST解析 // 匹配 throw new Error(...) 和 throw ... / throw ... const nativeErrorRegex /throw\snew\sError\(/gi; const throwStringRegex /throw\s[][^]*[]/gi; let match; const lineOffsets []; // 存储每行的起始索引用于计算行号 let currentIndex 0; for (let i 0; i lines.length; i) { lineOffsets[i] currentIndex; currentIndex lines[i].length 1; // 1 for newline character } // 检查原生 Error while ((match nativeErrorRegex.exec(content)) ! null) { const lineNumber findLineNumber(match.index, lineOffsets); const column match.index - lineOffsets[lineNumber] 1; // 4. 报告违规 await createViolation({ file: file.path, line: lineNumber 1, // 转换为1-based行号 column: column, message: 禁止直接使用 new Error()。请使用 AppError 类。, ruleId: no-native-error, }); } // 检查抛出字符串 while ((match throwStringRegex.exec(content)) ! null) { const lineNumber findLineNumber(match.index, lineOffsets); const column match.index - lineOffsets[lineNumber] 1; await createViolation({ file: file.path, line: lineNumber 1, column: column, message: 禁止直接抛出字符串错误。请使用 AppError 类。, ruleId: no-native-error, }); } } }, }); // 辅助函数根据字符索引找到对应的行号 function findLineNumber(index: number, lineOffsets: number[]): number { for (let i 0; i lineOffsets.length - 1; i) { if (index lineOffsets[i] index lineOffsets[i 1]) { return i; } } return lineOffsets.length - 1; }这个规则虽然使用了相对简单的文本匹配但已经能捕获大部分常见违规。在实际项目中你可能会使用像typescript-eslint/parser这样的工具来生成 AST抽象语法树进行更精准、更复杂的模式匹配。3.5 运行检查与查看结果保存好规则文件后在项目根目录运行检查命令archgate check # 或者如果你是作为开发依赖安装的 npx archgate check如果代码中存在throw new Error(Something went wrong)你会看到类似如下的输出❌ Violations found: src/services/userService.ts:45:12 no-native-error: 禁止直接使用 new Error()。请使用 AppError 类。 src/utils/validator.ts:18:5 no-native-error: 禁止直接抛出字符串错误。请使用 AppError 类。 × 2 violations (2 files)输出清晰地指出了违规的文件、行号、列号和具体规则。现在你可以根据提示去修复这些代码。修复完成后再次运行archgate check如果输出没有显示违规则表示检查通过。4. 规则编写进阶从模式匹配到AST分析基础的文本匹配规则对于简单的模式很有效但对于复杂的代码结构如检查导入语句、验证函数签名、确保装饰器的使用等就显得力不从心且容易误报。这时我们需要利用代码的抽象语法树AST。4.1 使用AST解析器增强规则以检查“禁止直接导入某个内部模块而应通过公共接口导入”为例使用typescript-eslint/parser可以更可靠地实现。首先确保你的项目或规则环境安装了必要的依赖npm install -D typescript-eslint/parser typescript-eslint/typescript-estree然后编写一个更强大的规则// .archgate/adrs/ARCH-002-internal-module-access.rules.ts import { defineRule, RuleContext } from archgate/cli; import { parse } from typescript-eslint/parser; import type { TSESTree } from typescript-eslint/typescript-estree; export default defineRule({ meta: { id: no-direct-internal-import, description: 禁止直接导入内部模块 myapp/internal/*应通过 myapp/public-api 导入, }, async check(ctx: RuleContext) { const { glob, createViolation } ctx; const files await glob([src/**/*.ts, src/**/*.tsx]); for (const file of files) { const content await file.text(); let ast: TSESTree.Program; try { // 解析文件为AST ast parse(content, { loc: true, // 保留位置信息 range: true, // 保留字符范围 ecmaVersion: latest, sourceType: module, }); } catch (error) { // 如果解析失败可能是语法错误跳过该文件或报告解析错误 console.warn(Failed to parse ${file.path}:, error.message); continue; } // 遍历AST寻找 ImportDeclaration 节点 traverseAST(ast, (node) { if (node.type ImportDeclaration) { const importSource node.source.value; // 检查是否直接导入了内部模块 if (typeof importSource string importSource.startsWith(myapp/internal/)) { // 报告违规 createViolation({ file: file.path, line: node.loc!.start.line, column: node.loc!.start.column 1, message: 禁止直接导入内部模块 ${importSource}。请改为从 myapp/public-api 导入相应的公共接口。, ruleId: no-direct-internal-import, }); } } }); } }, }); // 一个简单的递归AST遍历函数 function traverseAST(node: TSESTree.Node, visitor: (node: TSESTree.Node) void) { visitor(node); for (const key in node) { if (node[key as keyof TSESTree.Node] typeof node[key as keyof TSESTree.Node] object) { const child node[key as keyof TSESTree.Node]; if (Array.isArray(child)) { child.forEach((n) n traverseAST(n, visitor)); } else if (child (child as TSESTree.Node).type) { traverseAST(child as TSESTree.Node, visitor); } } } }这个规则通过 AST 分析能精准地定位import语句避免了正则表达式可能匹配到字符串或注释中的相似文本的问题。4.2 规则的组织与复用随着项目增长规则可能会变多。好的组织方式能提升可维护性。1. 按领域/模块分组你可以在.archgate/adrs/下创建子目录如frontend/、backend/、api/将相关的 ADR 和规则文件放在一起。Archgate CLI 默认会递归查找.rules.ts文件所以这种结构是被支持的。2. 提取公共工具函数如果多个规则共享类似的逻辑如特定的 AST 遍历、路径匹配逻辑可以将其提取到.archgate/目录下的一个共享工具文件中。// .archgate/utils/ast-helpers.ts import { parse } from typescript-eslint/parser; import type { TSESTree } from typescript-eslint/typescript-estree; export async function parseFileToAST(filePath: string, content: string): PromiseTSESTree.Program | null { try { return parse(content, { loc: true, range: true, ecmaVersion: latest, sourceType: module }); } catch (error) { console.warn(AST parsing failed for ${filePath}:, error.message); return null; } } export function findImports(ast: TSESTree.Program): TSESTree.ImportDeclaration[] { const imports: TSESTree.ImportDeclaration[] []; // ... 实现遍历逻辑 return imports; }然后在规则文件中导入并使用这些函数。3. 规则配置化对于需要根据不同项目或环境调整参数的规则可以考虑将配置外置。例如在一个.archgate/config.json文件中定义要检查的路径模式、要忽略的文件等然后在规则中读取这个配置。{ rules: { no-direct-internal-import: { internalPathPattern: myapp/internal/, publicApiPath: myapp/public-api, exclude: [**/*.test.ts, **/*.spec.ts] } } }4.3 性能考量与最佳实践增量检查在 CI 中可以对拉取请求中变更的文件进行增量检查而不是全量扫描。Archgate CLI 本身可能不直接支持但你可以结合git diff命令来获取变更文件列表然后只对这些文件运行规则检查前提是你的规则逻辑支持只检查特定文件。缓存AST解析大型代码库的 AST 可能比较耗时。对于非常复杂的规则集可以考虑将解析后的 AST 缓存起来避免对同一个文件重复解析。不过这需要更复杂的规则生命周期管理。规则粒度尽量保持规则的单一职责。一个规则只检查一件事。这样不仅易于理解和维护也便于在特定场景下禁用某个规则。清晰的错误信息createViolation中的message字段应明确指出问题所在并最好给出修复建议或指向相关 ADR 文档的链接。5. 集成到开发工作流与CI/CD规则写好了但只有融入开发流程才能发挥最大价值。目标是让架构检查变得“无形”——在问题发生前自动拦截。5.1 集成到 Git 预提交钩子 (Pre-commit Hook)使用像husky这样的工具可以轻松管理 Git 钩子。安装 husky:npm install -D husky npx husky init这会在项目根目录创建.husky/目录并在package.json中添加prepare脚本。添加 pre-commit 钩子: 编辑.husky/pre-commit文件添加 Archgate 检查#!/usr/bin/env sh . $(dirname -- $0)/_/husky.sh echo Running architecture compliance check... npx archgate check现在每次执行git commit时都会自动运行archgate check。如果发现违规提交会被中止开发者必须修复这些问题后才能提交。实操心得在预提交钩子中运行所有规则可能会拖慢提交速度尤其是项目很大时。一个折中方案是只对暂存区staged的文件运行检查。这需要写一个脚本先用git diff --cached --name-only获取暂存文件列表然后筛选出需要检查的文件类型如.ts,.js最后将这些文件路径作为参数传递给一个定制的检查脚本。这个定制脚本需要调用 Archgate 的底层 API 或对规则进行适配目前 Archgate CLI 可能不直接支持文件过滤但这是社区常见需求未来版本可能会支持。5.2 集成到持续集成流水线这是强制执行架构规范的“最后防线”。以 GitHub Actions 为例# .github/workflows/archgate-check.yml name: Architecture Compliance Check on: pull_request: branches: [ main, develop ] push: branches: [ main ] jobs: archgate-check: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkoutv4 - name: Setup Node.js uses: actions/setup-nodev4 with: node-version: 20 cache: npm - name: Install dependencies run: npm ci # 使用 ci 命令确保依赖锁一致 - name: Run Archgate Check run: npx archgate check将这个工作流文件放入仓库的.github/workflows/目录。每当有新的拉取请求PR推送到main或develop分支或者直接向这些分支推送代码时这个任务就会运行。如果archgate check发现任何违规退出码为1整个 CI 任务会失败并在 PR 界面上显示失败状态从而阻止合并。进阶技巧缓存与性能为了加速 CI 运行你可以缓存 Archgate 的安装如果全局安装或项目的node_modules。- name: Cache node modules uses: actions/cachev4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles(**/package-lock.json) }} restore-keys: | ${{ runner.os }}-node-5.3 与AI编辑器插件集成可选但强大如果你或你的团队使用 Claude Code 或 CursorArchgate 的插件能将 ADR 的效力提升到另一个维度。登录与授权archgate login这通常会打开浏览器让你用 GitHub 账号授权。这步是为了将你的项目与 Archgate 的云服务用于插件同步关联。注意核心的 CLI 检查功能完全不需要登录登录仅用于插件的高级功能。初始化插件 在已运行过archgate init的项目中插件通常会自动安装或提示你安装。你也可以参考官方文档进行手动配置。体验“架构感知”的AI编程 安装插件后当你让 AI 助手如 Claude编写一个函数时它会自动参考你项目中的 ADR。例如如果你有“所有 API 响应必须包裹在标准格式中”的 ADRAI 在生成 API 控制器代码时可能会主动使用你定义的ApiResponse包装器而不是随意返回一个对象。 同样当你尝试让 AI 修改一段受规则约束的代码时它可能会提醒你“根据项目中的 ARCH-003 决策这里应该使用 X 模式需要我按此调整吗”这个闭环极大地提升了开发效率和质量一致性尤其对于不熟悉项目历史或架构细节的新成员。6. 实战案例为微服务项目定义通信规范让我们通过一个更复杂的实战案例看看 Archgate 如何管理一个具体的、跨组件的架构决策。场景一个基于 Node.js 的微服务项目有多个服务user-service, order-service, payment-service。我们决定服务间同步通信统一使用 gRPC异步事件通信使用 Apache Kafka并且所有事件必须遵循特定的 Avro 模式。6.1 创建 ADR 文档首先创建 ADR 文档记录这个决策。--- id: ARCH-003 status: accepted date: 2023-11-15 deciders: [架构委员会] tags: [microservices, communication, grpc, kafka] --- # 3. 微服务间通信协议与标准 ## 上下文与问题陈述 随着服务数量增加服务间通信方式变得杂乱HTTP/REST、直接数据库调用、自定义TCP等导致接口不一致、维护困难、监控缺失和耦合度增加。需要统一通信标准以提升系统的可维护性、可观测性和解耦程度。 ## 决策结果 1. **同步通信**强制使用 **gRPC**。所有服务必须通过 .proto 文件定义接口并生成强类型客户端/服务端代码。 2. **异步通信事件**强制使用 **Apache Kafka**。服务间解耦通过发布/订阅事件实现。 3. **事件格式**所有通过 Kafka 传递的消息体必须是 **Avro** 格式并使用中央模式注册表如 Confluent Schema Registry进行序列化/反序列化。禁止使用 JSON 直接作为消息体。 ## 约束与例外 * **对外公开的API**面向外部客户或前端BFF的 API 仍可使用 HTTP/REST/GraphQL不受此决策限制。 * **服务发现**gRPC 客户端应使用基于 Consul 的服务发现而非硬编码地址。 * **Avro模式管理**所有 Avro 模式定义必须存放在 ./schemas/ 目录下并通过 CI 自动同步到模式注册表。6.2 编写配套的复合规则这个决策涉及多个方面我们可以编写一个规则文件来检查这些约束。// .archgate/adrs/ARCH-003-service-communication.rules.ts import { defineRule, RuleContext } from archgate/cli; import { globby } from globby; // 可能需要单独安装 import * as fs from fs/promises; import * as path from path; export default defineRule({ meta: { id: service-communication-standard, description: 强制执行微服务通信标准gRPC for sync, KafkaAvro for async., }, async check(ctx: RuleContext) { const { createViolation } ctx; // 检查1: 是否存在非gRPC的显式HTTP服务端代码如Express/Koa直接监听 const serverFiles await globby([src/**/*.ts, src/**/*.js], { ignore: [**/*.test.*, **/*.spec.*, **/client/*, **/test/**], }); for (const filePath of serverFiles) { const content await fs.readFile(filePath, utf-8); // 简单检查是否引入了 express/koa/fastify 并创建了服务器这是一个简化示例实际需要AST if ( (content.includes(require(\express\)) || content.includes(from express)) (content.includes(.listen() || content.includes(new Server()) ) { // 排除明确允许的对外API服务比如标记为external-api的文件夹 if (!filePath.includes(/external-api/)) { await createViolation({ file: filePath, line: 1, // 简化处理实际应定位到具体行 column: 1, message: 检测到可能用于内部服务的HTTP服务器Express/Koa等。根据 ARCH-003内部同步通信应使用 gRPC。如果这是对外API请将其移至 external-api 目录下或添加特殊注释忽略此规则。, ruleId: service-communication-standard, }); } } } // 检查2: 检查Kafka生产者/消费者是否使用了Avro序列化 const kafkaFiles await globby([src/**/*kafka*.ts, src/**/*producer*.ts, src/**/*consumer*.ts], { ignore: [**/*.test.*, **/*.spec.*], }); for (const filePath of kafkaFiles) { const content await fs.readFile(filePath, utf-8); // 检查是否使用了KafkaJS等库但未使用Avro序列化器 const usesKafka content.includes(kafkajs) || content.includes(nestjs/microservices) || content.includes(kafka); const usesAvro content.includes(avro) || content.includes(SchemaRegistry) || content.includes(Confluent); if (usesKafka !usesAvro) { // 进一步检查是否在配置中明确设置了序列化器这里只是简单演示。 await createViolation({ file: filePath, line: 1, column: 1, message: 检测到Kafka相关代码但未明确使用Avro序列化。根据 ARCH-003所有Kafka消息必须使用Avro格式。请确保使用了类似 kafkajs-avro 或 Confluent 的 Schema Registry 客户端。, ruleId: service-communication-standard, }); } } // 检查3: 确保 proto 文件存在且符合命名规范 const serviceDirs await globby([src/services/*, src/modules/*], { onlyDirectories: true }); for (const dir of serviceDirs) { const protoFiles await globby([path.join(dir, **/*.proto)]); if (protoFiles.length 0) { // 如果这个目录看起来是一个服务目录例如有index.ts或package.json但没有proto文件则警告 const hasEntry await globby([path.join(dir, index.ts), path.join(dir, index.js), path.join(dir, package.json)]); if (hasEntry.length 0) { await createViolation({ file: dir, // 报告目录 line: 1, column: 1, message: 服务目录 ${path.relative(process.cwd(), dir)} 未发现 .proto 文件。根据 ARCH-003提供gRPC接口的服务必须定义proto文件。, ruleId: service-communication-standard, }); } } } }, });这个规则文件展示了如何将一条复杂的架构决策分解为多个具体的自动化检查点。虽然有些检查如判断是否为内部服务可能有些粗糙但它为团队提供了明确的、自动化的守门员。6.3 管理规则的例外与豁免任何规则都可能存在合理的例外。Archgate 本身可能不提供内置的豁免机制但我们可以通过一些模式来实现。1. 注释标记忽略在代码中使用特定的注释来告诉规则检查器“忽略此处”。这需要在规则实现中处理。// 在规则检查逻辑中可以跳过包含特定注释的行 const lines content.split(\n); for (let i 0; i lines.length; i) { if (lines[i].includes(// archgate-ignore-next-line) || lines[i].includes(// archgate-disable-line)) { // 跳过下一行或当前行的检查 continue; } // ... 执行检查逻辑 }然后在代码中// archgate-ignore-next-line: service-communication-standard const app express(); // 这是一个特例允许使用Express用于本地调试端点2. 配置文件忽略列表在.archgate/下创建一个配置文件列出要忽略的特定文件、目录或代码模式。// .archgate/config.json { ignorePatterns: [ src/legacy/**/*, // 忽略整个遗留代码目录 **/*.d.ts, // 忽略类型声明文件 **/test-fixtures/** // 忽略测试夹具 ], ruleOverrides: { service-communication-standard: { exclude: [src/external-api/**/*] // 此规则不检查外部API目录 } } }规则在执行时需要先读取这个配置并过滤掉被忽略的路径。3. 基于上下文的动态判断如上面的规则示例通过检查文件路径是否包含/external-api/来判断是否应用规则。这是一种更智能的例外处理。7. 常见问题、排查与团队落地经验将 Archgate 引入团队并让其顺畅运行可能会遇到一些挑战。以下是一些常见问题和我个人的踩坑经验。7.1 安装与初始化问题问题在 CI 环境中通过curl | sh安装失败提示权限或网络错误。排查检查 CI 机器的网络出口确保能访问https://cli.archgate.dev。有些 CI 环境如 GitHub Actions默认不允许curl | sh这种管道执行远程脚本。解决首选方案将 archgate 作为项目的devDependency安装 (npm install -D archgate)然后在 CI 脚本中使用npx archgate check。这是最可靠、版本最可控的方式。次选方案在 CI 脚本中先下载二进制文件到临时目录然后指定完整路径运行。- name: Download Archgate run: | curl -L -o /tmp/archgate https://cli.archgate.dev/download/linux/x86_64 chmod x /tmp/archgate - name: Run Check run: /tmp/archgate check问题archgate init命令没有在项目中创建.archgate目录。排查确认当前目录是否是一个已初始化的 Git 仓库根目录或你指定的目录。检查是否有写权限。解决确保在正确的目录下执行。也可以尝试使用archgate init --force强制初始化。7.2 规则执行与性能问题问题archgate check运行非常慢尤其是在大型代码库上。排查检查规则文件中的glob模式是否过于宽泛如**/*扫描了不必要的文件node_modules,.git,dist等。规则中是否进行了昂贵的操作如对每个文件都进行完整的 AST 解析解决精确指定文件范围在规则中使用更具体的 glob 模式如[src/**/*.ts, lib/**/*.ts]并利用ignore选项。const files await glob([src/**/*.ts, lib/**/*.ts], { ignore: [**/*.test.ts, **/*.spec.ts, **/node_modules/**, **/dist/**] });缓存与惰性解析对于复杂的 AST 规则考虑实现一个简单的缓存机制或者只在文件内容匹配到某些关键词如import时才进行 AST 解析。并行处理如果规则是独立的可以考虑使用Promise.all并行处理多个文件注意 Archgate 上下文ctx的线程安全性。问题规则报告了误报False Positive或漏报False Negative。排查这是规则逻辑不够精确导致的。检查规则的正则表达式或 AST 匹配逻辑是否覆盖了所有边界情况。解决完善规则逻辑使用更精确的 AST 查询替代简单的文本匹配。例如用typescript-eslint的ESLintUtils可以更可靠地定位节点。添加更多测试用例为你的.rules.ts文件编写单元测试模拟各种代码场景确保规则行为符合预期。你可以使用 Jest 或 Mocha 来测试规则函数。引入置信度在规则中如果匹配模式比较模糊可以输出warning级别的违规而不是error或者通过注释让开发者确认。7.3 团队协作与流程集成问题问题团队成员觉得规则太严格阻碍了开发速度抵触使用。解决渐进式推行是关键。从少数核心规则开始不要一开始就上几十条规则。挑选 2-3 个最能体现架构价值、且团队共识度最高的决策如“错误处理规范”、“API 响应格式”先为它们编写规则。先警告后阻断在 CI 中可以先让archgate check运行但不失败例如只输出报告不影响合并让团队有一个适应期观察违规情况。提供快速修复路径当规则报错时错误信息应清晰并最好能提供自动修复的建议或命令虽然 Archgate 本身可能没有--fix功能但你可以编写配套的 codemod 脚本。将 ADR 评审作为流程一部分当创建新的 ADR 及其规则时必须经过团队的技术评审。这确保了规则本身是合理且被广泛接受的。问题.archgate/adrs/目录下的 ADR 和规则文件如何做版本管理和评审解决将它们视为架构即代码。和源代码一样对 ADR/Rules 的修改应该通过拉取请求PR进行并需要其他成员的审查。在 PR 描述中说明修改 ADR 或规则的原因是新增决策、修正错误还是放宽约束。审查者需要仔细检查规则逻辑是否正确是否会产生意想不到的副作用。可以考虑在 CI 中为.archgate/目录的变更也运行一些检查例如确保新增的.rules.ts文件语法正确或者 ADR 的 YAML 头格式符合规范。7.4 高级技巧与扩展思路自定义报告格式archgate check默认的输出格式可能不适合你的 CI 界面。你可以编写一个简单的脚本解析 Archgate 的输出可能是 JSON 格式如果支持的话并将其转换为你的 CI 平台如 GitHub Checks, GitLab Merge Request widgets所需的格式。与代码质量工具结合Archgate 专注于架构约束它可以和 ESLint代码风格、Prettier代码格式化、SonarQube静态分析等工具协同工作。在package.json的scripts中定义一个复合命令scripts: { lint: eslint ., format:check: prettier --check ., archgate: archgate check, ci:check: npm run lint npm run format:check npm run archgate }动态规则规则不一定非要写死在.rules.ts文件里。你可以从外部配置如数据库、API动态加载规则列表。这适用于大型组织希望集中管理架构规范。不过这需要更复杂的 Archgate 客户端定制。将 Archgate 引入项目起初可能会觉得增加了额外的工作量——要写文档还要写“检查文档的代码”。但长远来看它通过自动化将架构治理从一种靠自觉和 Review 的“软约束”变成了一个客观、即时反馈的“硬约束”。它迫使团队在做出架构决策时思考得更周全因为要能转化为可执行的规则也极大地降低了知识传递的成本和错误。当新成员提交的代码因为违反一个他从未读过的 ADR 而被自动拒绝时他学到的不仅是一条规则更是团队对工程质量的严肃态度。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2587169.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!