10 KiB
项目开发
新建最小项目:
dever new my_app
dever fmt my_app --check
dever check my_app
dever test my_app
dever run my_app -- hello.greeting.greet '{"name":"Dever"}'
dever build my_app --output my_app/program
./my_app/program hello.greeting.greet '{"name":"Dever"}'
创建 Markdown 项目用 dever new my_app --markdown。两种模板使用相同代码和行为,Markdown 另加编译器校验的说明。目标目录必须不存在,父目录必须已存在;不接受向现有项目合并或覆盖。build 输出也必须不存在。
模板只包含 CMD、App、配置、一个测试和开发指引,无需数据库或 HTTP 服务。实际项目在它的目录合同上扩展,替换演示领域,删除不再使用的示例与测试。添加 HTTP 时须完成 站点与身份配置,不能只把 cmd 换成 get。
my_app/
AGENTS.md
README.md
config/setting.json
module/
news/article/
app.dever
domain.dever
model.dever
port.dever
adapter.dever
job.dever
api/
admin/manage.dever
front/browse.dever
test/news/article/publish.dever
这是可用职责位置,不是要求创建全部文件。component、domain 和文件主题使用有业务意义的 ASCII 小写蛇形命名;类型用 PascalCase,方法和字段用 snake_case。不要增加任意第三层领域目录。组件按业务能力划分,领域按一致的数据和业务边界划分。组件、领域和主题不能命名为 common/shared/utils/helper/helpers/base/service/services 等编译器禁止的通用桶。
| 位置 | 责任和访问规则 |
|---|---|
| App | 跨领域公开能力,校验与业务编排;同领域用 app.publish,外部用 news.article.publish |
| Domain | 同领域内部业务规则;纯计算优先放这里,不承接数据库/外部 I/O |
| Model | 存储字段、索引、关系、迁移、Seed、静态 SQL;同领域 App 使用 ORM |
| Port | 外部能力的类型、操作与失败合同 |
| Adapter | 实现 Port;私有,不反调 App/Domain/Model |
| API | HTTP/CMD 入口绑定与自动 REST,无业务函数体 |
| Job | 持久任务声明,绑定同领域 App |
| Test | 文件同名、零输入零输出的唯一测试入口 |
文件与函数组织
简单领域保留角色单文件;复杂角色可以拆为 app/publish.dever、app/search.dever 等内聚主题,调用名不带文件名。非 API topic 目录不递归。App/Domain/Port/Adapter/Job 单文件与同名目录二选一,选择目录时至少有两个内聚主题文件;Model/API 主文件与 topic 目录可按编译器合同共存,不受这个最少文件数限制。只有 api/ 可按站点和资源继续嵌套,目录会影响 URL。
一个文件可以包含多个相关类型、函数和分句。按完整业务主题命名,不机械地一个函数一个文件,也不为尚不存在的需求创建空 App、Domain、Port 或 Adapter。
抽取函数应表达独立业务规则、维护明确的不变量、复用已有逻辑,或隔离难以理解的复杂流程。只使用一次的函数也可以有这些作用;不能只因几行代码能起名字就再加一层。职责内聚的代码不按行数强拆,也不通过把所有逻辑塞进一个长函数来规避碎片化。
| 情况 | 处理方式 |
|---|---|
parse_id 重新手写数字解析并把失败悄悄变成 0 |
复用已有解析、名义 ID 和明确失败合同,不复制算法 |
trim_name 只原样转发一次标准 trim,没有新增合同 |
在使用处调用标准能力 |
page_slice 重复 Model 已有的分页规则 |
使用 Model 分页,不在各领域复制一份 |
publish 组织发布条件、状态变化和后续任务 |
保留完整 App 能力,把内聚业务规则放同领域 Domain |
| 单次使用的规则需要独立表达有效期或状态不变量 | 可以抽取并按业务含义命名 |
| 两处相似代码承担不同规则、未来变化原因不同 | 保持各自归属,不为表面相似增加通用调度层 |
相同规则先复用现有所有者;只有真实共享的逻辑才抽象。跨领域共享能力由负责该业务的 App 提供,不直接调用对方私有实现,不为缩短调用名建立空转发链。普通 CRUD 直接用 Model 和 rest model;需要超出 Model 字段合同的输入转换、业务校验、关联操作或输出合同才增加 App 编排,具体见 应用规则。
配置与硬编码
| 数据或规则 | 归属 |
|---|---|
| 随部署变化的服务地址、端口、数据库连接、凭据 | config/setting.json,不增加环境变量配置,不提交真实秘密 |
| 当前用户、租户、权限及资源归属 | 可信认证上下文、核心授权与 Model 策略,不接受客户端自报身份 |
| 有固定业务含义的状态、范围和规则 | 类型、互斥分句、字段约束或内聚 Domain 定义,复用同一规则 |
| 需要用户或运营维护的业务数据 | 项目 Model 与相应业务流程,不把具体记录写进源码分支 |
| 测试 ID、样例地址和固定结果 | 测试夹具,不作为生产失败时的兜底 |
禁止按某个账号、租户、记录的裸 ID、名字或地址开特例,例如用 user_id == 1 代替管理员授权。普通查询参数可以标识目标记录,但不能因此成为操作者身份或访问权限。
避免硬编码不等于搬走所有字面量。局部的 0、1、提示文字和稳定协议常量可以保留;有业务意义、难以理解或反复出现的值由现有类型或领域定义表达。只有确需运行时变化的值才增加配置。官方发行源是工具链固定合同,不能塞进业务 setting.json。
禁止绕过检查
- 不添加假入口、无意义调用或仅供测试触达的调用,掩盖生产入口不可达的 App/Domain。删除本次废弃代码或接通真实业务调用链。
- 不把重复业务逻辑转移到 Adapter、Python、JS 或 Go 来避开 Dever 检查。外部实现的质量同样需要审查。
- 不吞掉失败、用空值或假数据冒充成功;有业务依据的恢复须使用明确结果合同,并验证恢复行为。
- 不为通过检查擅自改
dever.api、删失败测试、弱化断言或关闭权限、类型及沙箱限制。确需改变公开合同,先说明调用方影响,在用户授权范围内同步实现、基线与测试。 - 普通应用开发不修改编译器或核心库来容纳违规代码;语言或核心维护须有相应任务授权。只处理当前改动相关问题,不借机重写无关代码。
测试与检查
test/hello/greeting/greet.dever:
greet() () {
assert_eq(hello.greeting.greet("Dever"), "Hello, Dever")
}
模板中的实际字符串以生成文件为准。测试只执行文件同名函数,不需要 main 或注解。assert 接受 Bool,assert_eq 要求同类型。测试可访问同领域 App/Domain,跨领域仅 App,不能直接写 Model CRUD。使用 Model 时运行器使用临时 SQLite;不使用 Model 时不需要数据库。它不会读取生产 setting.json 或生产 data。对业务失败使用 result(call) 配合 choice 分句验证。
常用命令:fmt --check 查格式,check 查语法/类型/目录/分句/配置,api 输出 API 合同,test 运行应用套件,run 开发执行,build 生成独立程序。不要发明 test --filter、run api 或 dever init。
验证范围
| 检查 | 能证明的范围 |
|---|---|
dever fmt <root> --check |
源码格式;不证明类型或业务正确 |
dever check <root> |
静态语法、类型、目录、命名、分句、相关配置、已有 API 基线,以及应用入口可达性和冗余硬检查;不执行应用 |
dever test <root> |
当前项目全部应用测试的实际断言;没有筛选参数,不证明生产入口完整 |
dever run/build |
通过与 check 相同的应用硬检查,再准备本地编译资源并执行或编译;成功不等于部署或性能验收 |
| AI 审查 | 业务含义、职责拆分、复用、硬编码与验证范围,不能替代程序检查 |
check/run/build 共用 C016,拒绝生产入口不可达的 App/Domain,以及符合现有判定条件的私有空转发和 Model CRUD 包装;并非任意角色、所有函数或全部 CRUD 包装都能检出。check 不准备 Worker 或 runtime pack,不进行原生编译,不启动应用、服务或数据库。test 保持独立的测试入口与 Port fake 检查,不执行生产入口可达性检查;不能用测试调用掩盖生产死代码,也不能把静态检查当作运行验收。
W001–W005 等诊断包含未使用赋值、部分冗余条件、相同私有实现、重复纯调用和空转发建议,警告本身不会使命令失败。审查本次引入的诊断,修正有证据的问题;不能声称编译器能判定全部硬编码或业务价值。skill 中的开发要求不自动变成编译器硬错误。
选择与改动相符且已获授权的检查。新模板的小套件可以完整运行;已有大项目不默认运行全量 dever test,也不默认启动服务、连接真实数据库或压测。需要但尚未运行的检查要说明原因和剩余证据,不能伪报通过。已有 dever.api 时保留基线约束;dever api <root> --output <新文件> 可以导出待比较合同,不覆盖原文件。
源码放 module/,测试放 test/,部署配置放 config/,运行数据放 data/。生成程序从二进制同级 config/setting.json 读取配置。应用无需打包编译器;使用外部 Lib 时相关 runtime/依赖由 build 嵌入。只读 check 不证明真实数据库或部署验收。
核心与业务的区别
源码仓库 中,crates/dever-core 负责语言检查/编译,crates/dever-runtime 负责运行时,crates/dever-cli 负责命令,library/ 是随版本提供的标准 Dever 源码。维护中的完整业务示例是 examples/cms/dever 与 examples/cms/md;examples/old 是历史回归,不作新项目模板。
开发普通应用无需复制或修改这些核心目录。可复用业务组件作为 Dever Package;外部 Python/JS/Go 生态称 Lib。多个项目共享机器安装的 Dever。