深入了解 DenoX:Deno 中的文件路由、MVC 分片以及 AGENTS.md 合约
DenoX框架如何将Hono、基于文件的路由机制、功能切片、全局安全中间件以及以规范优先的AGENTS.md工作流整合起来,用于构建AI编码代理。
在后台项目中,服务器的配置往往并非最关键的部分,真正重要的是功能的实现。Rails、Laravel和Next.js都通过代为处理各种底层细节吸引了开发者,而Deno凭借其默认的安全权限、原生的TypeScript支持以及内置的工具集,同样具备成为此类框架的潜力。DenoX是一个基于Hono开发的开源全栈框架,旨在充当这样的规范层。它针对常见架构问题给出的解决方案,以及为AI编码工具整理的方案模板,都是可在任何TypeScript后台项目中重复使用的模式。
规范层所填补的空白
Deno 2带来了对npm的兼容性、JSR注册表、成熟的标准库,以及一个能够执行代码检查、格式化、测试、编译和打包的单一二进制文件(详情可参阅Deno 2.x如何解决Node兼容性与工具使用难题)。但纯运行时环境无法解决各团队之间在以下问题上的分歧:业务逻辑应放在何处、错误如何报告、谁来验证配置以及速率限制应设置在哪里。
DenoX通过一个原则来解决这些问题:以约定优于配置为准则,并通过工具加以验证。有文档记载的约定可能会发生变化,而通过CI流程检查过的约定则保持不变。
基于文件的路由系统及自动生成并提交的路由表
路由来自文件系统。在pages目录下添加一个文件即可创建一个URL,其中的括号内容将成为参数:
src/frontend/pages/
├── index.ts → /
├── about/main.ts → /about
├── users/main.ts → /users
└── posts/[id].ts → /posts/:id
发现过程并非在运行时进行。执行 deno task routes 会遍历路由树并生成静态的、确定的路由表。有两个因素使得该机制十分稳健:
- 静态路由总是先于动态路由被注册,因此
/users/new永远不会被/users/:id所覆盖。在像 Hono 这样的首匹配路由框架中,注册顺序决定了路由行为,而预先生成路由表则避免了常见的隐性错误。 - 生成的路由表文件会被提交到版本控制仓库中,如果该文件过时,持续集成流程就会失败。
页面即普通函数
页面实际上就是一个普通的 TypeScript 模块。它会导入 Hono 的 Context 类型以及一个用于转义 HTML 字符的辅助函数:
import type { Context } from "hono";
import { escapeHtml } from "@/shared/html.ts";
随后它会导出一个 config 对象,用于指定布局以及一个返回 HTML 字符串的默认函数:
export const config = { layout: "default" } as const;export default function homePage(c: Context): string {
const name = escapeHtml(c.req.query("name") ?? "world");
return `<h1>Hello, ${name}!</h1>`;
}
值得注意的细节是escapeHtml。name查询参数由用户控制,若直接将其插入到标记中,就会形成典型的反射型XSS漏洞。在DenoX中,对不可信输入进行转义并非建议,而是写入项目工程规范中的强制要求。由于该模板引擎不会自动对字符串进行转义,因此所有外部数据的插入操作都必须通过这个辅助函数来完成。
结构固定的功能模块
在API层面,每个功能都是一个独立的模块,包含相同的文件集合,每份文件仅承担一项任务:
src/api/users/
├── user.model.ts entities only
├── user.dto.ts unknown → typed DTO (boundary validation)
├── user.repository.ts interface + default implementation
├── user.service.ts business rules only — no HTTP, no HTML
├── user.controller.ts HTTP adapter only
└── user.routes.ts composition root (constructor injection)
DTO模块在边界处将未知的输入转换为带类型的对象,因此堆栈中更深入的部分无需处理原始请求体。服务层仅包含业务规则,对HTTP或HTML一无所知。控制器则是轻量级的HTTP适配器。路由文件是依赖注入的起点,所有依赖都通过构造函数注入的方式连接起来。
服务层依赖于仓库接口而非具体类。因此,若要将内存存储替换为Postgres或Deno KV,就需要为每个功能修改一个文件。
作为带类型异常的错误处理
业务规则通过抛出带类型的异常来指示失败。下面的服务方法会拒绝使用已存在的邮箱创建第二个用户:
async create(dto: CreateUserDto): Promise<User> {
const existing = await this.repository.findByEmail(dto.email);
if (existing !== null) {
throw new ConflictException(`Email "${dto.email}" is already registered`);
}
return await this.repository.create(dto);
}
该服务既不选择状态码也不格式化响应。一个集中的错误处理机制会将每种异常类型映射为统一的JSON结构,并确保堆栈跟踪信息不会传递给客户端。需要注意的是,将邮件内容原样返回会导致账户枚举,因此在公共接口上应避免这种做法。
一次实现安全机制,处处适用
跨功能的安全保护措施集中在全局中间件中,而非各个功能模块中:包括内容安全策略、强化的响应头、CORS规则、基于源地址的CSRF检测、按客户端IP设定的速率限制、消息体大小上限、超时设置以及内部错误的屏蔽处理。各功能模块只需使用这些机制,而无需重复实现。
配置处理方式相同。每个环境变量都会在进程启动时被解析、验证并固定,一旦缺少或格式有误,应用就会拒绝启动。在生产环境中,CORS_ORIGIN=*会直接被拒绝。在启动阶段就快速发现问题,总比在生产环境中调试配置不完整的服务要好。
一个命令背后的三层测试
测试机制不仅仅停留在简单的断言层面:
- 单元测试通过调用记录模拟对象来检测纯逻辑部分,完全不需要任何Deno权限。
- 集成测试通过
app.request()来测试完整连接的应用,可验证状态码、响应内容甚至安全头信息,而无需建立套接字。
Deno.serve,并通过真实的 fetch 请求对其进行调用,其中还包括一个故意触发速率限制的请求,以确认其会返回 429 状态码。完整的质量检测流程涵盖了格式检查、代码规范检查、过期路由表检测、严格的类型检查以及所有测试层级,可通过 deno task ci 执行,而 GitHub Actions 流水线也会按此顺序运行。
一个部署命令,无需处理凭证
该仓库包含了 Fly.io、Railway、Render、Docker 的配置文件,以及为 VPS 设计的强化版 systemd 单元文件,同时还提供了对 Deno Deploy 的一流支持。只需一个任务即可列出目标、进行模拟部署或实际执行部署操作:
deno task deploy # list targets
deno task deploy fly # dry run: steps + env reminders
deno task deploy fly --run # execute (auth delegated to the platform CLI)
该部署工具刻意不处理任何凭证信息。它会检查前置条件,展示执行计划并提示所需的环境变量,而将身份验证工作交由各平台的官方CLI处理。所有敏感信息完全不会进入框架内部。
作为可执行工程契约的AGENTS.md
DenoX最独特之处在于项目根目录中存在的AGENTS.md文件,它被视作人类贡献者与AI编程智能体都必须遵守的权威契约。该文件规定了技术栈版本、定义了标准的目录结构,并列出了那些绝不能重复开发的共享基础组件:日志记录器、异常层次结构、响应封装体以及配置模块。
它还明确了基于规范驱动的开发流程:
- 像
specs/feature.md这样的规范在编写时会标注status: draft。 - 由人工审核后将其状态改为
status: approved。 - 只有获得批准后,工作才能进入架构设计、计划制定、实现编码、测试以及文档编写等阶段。
系统会明确要求智能体在规范编写完成后停止工作并等待人工批准,因此智能体不能自行批准自己的计划后再修改一半的代码库。用户管理相关的完整参考流程向智能体展示了标准做法,而持续集成系统则会机械地强制执行这些规范——一旦生成的文件被手动修改,构建就会失败。
随着智能体编写更多代码,规范的意义仅在于能否通过自动方式进行检查;将合约与代码一起进行版本控制,并借助持续集成机制加以保障,就能将指导原则转化为约束规则。关于助手指令文件的相关内容,请参阅Vercel针对React最佳实践的AGENTS.md技能。
在本地运行
克隆仓库,根据示例创建环境文件,然后启动开发服务器:
git clone https://github.com/olavomello/denox.git
cd denox
cp .env.example .env
deno task dev
接着打开 http://localhost:8000,调用 /api/users,故意发送无效数据,检查错误响应是否保持整洁且不包含堆栈跟踪信息。也有实时演示版本可供查看。该项目采用MIT许可证;其规划中的功能包括Deno KV和Postgres适配器、自动布局注册、专用命令行工具、认证模块以及OpenAPI生成功能,所有这些都将遵循相同的以规范为先的工作流程。在采用该项目之前,请先查看其仓库中的当前状态。
关键要点
- 在构建时生成路由表,先注册静态路由再注册动态路由,将生成的文件提交版本控制,并让持续集成系统拒绝过期的文件。
- 为每个功能设定固定的结构:边界数据传输对象、基于接口的存储层、无需HTTP的服务以及简洁的控制器。
AGENTS.md 文件,规定规格需经过人工审批,并在持续集成过程中强制执行相关规则,从而让代理与人员都遵守这些规定。