Files

165 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目开发
新建最小项目:
```sh
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 服务。`dever new` 已生成 `AGENTS.md`;已有项目在根目录运行 `dever skill install` 即可写入同一份规则,保留文件中其他内容。
按需要替换模板的演示领域,删除不再使用的示例与测试。添加 HTTP 时须完成 [站点与身份配置](application.md),不能只把 cmd 换成 get。
```text
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 | 文件同名、零输入零输出的唯一测试入口 |
## 不擅自保留旧实现
默认只实现当前接口和语法。用户没有明确要求兼容时,不保留旧参数、旧配置读取、旧接口、别名或备用实现,不用“以后可能用到”作为保留理由。
改接口时,查清本项目调用方,一起修改实现和必要测试,删除本次废弃路径。外部调用方无法一起修改时,说明影响,由用户决定迁移安排;不能擅自加一套兼容流程。
项目通过 `dever.version` 使用某个编译器版本,是正常的版本选择,不要求业务代码同时支持新旧两套行为。业务明确要求的失败恢复也不是旧版本兼容,但必须有真实依据、明确结果和相应验证。
## 文件与函数组织
简单领域保留角色单文件;复杂角色可以拆为 `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 编排,具体见 [应用规则](application.md#modelcrud-与业务编排)。
## 配置与硬编码
| 数据或规则 | 归属 |
| --- | --- |
| 随部署变化的服务地址、端口、数据库连接、凭据 | `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`:
源码:[模板测试](../assets/template/test/hello/greeting/greet.dever)
```dever
greet() () {
message = app.greet("Dever")
assert_eq(message, "Hello, Dever")
}
```
测试只执行文件同名函数,不需要 main 或注解。`assert` 接受 Bool,`assert_eq` 要求同类型。业务失败用 `result(call)` 和 choice 分句断言。模板中的字符串以生成文件为准。
测试可调用同领域 App/Domain,跨领域只调用 App,不能直接写 Model CRUD。涉及 Model 时使用临时 SQLite,否则不需要数据库。命令会读取 setting.json 和锁中的依赖信息,但测试不连接部署数据库,也不使用项目 data。
常用命令:`fmt --check` 查格式,`check` 查语法/类型/目录/分句/配置,`api` 输出 API 合同,`test` 运行应用套件,`run` 开发执行,`build` 生成独立程序。不要发明 `run api` 或 `dever init`。
选一个测试运行:
```sh
dever test my_app --filter hello.greeting.greet --timeout-ms 5000
```
`--filter` 可以写组件、组件.领域或组件.领域.用例。`news.article` 不会选中 `news.article_extra`,不支持子串或通配符。筛选后仍检查全部源码;没有匹配项就报错,未执行用例显示 `not run`。
每例默认限时 60 秒,不含编译时间。`--timeout-ms` 必须是正整数毫秒;选项不能重复。超时后回收本例进程及后代,记录失败并继续下一例,最终命令返回失败。
### 验证范围
| 检查 | 能证明的范围 |
| --- | --- |
| `dever fmt <root> --check` | 源码格式;不证明类型或业务正确 |
| `dever check <root>` | 检查语法、类型、目录、配置和已有 API 基线;也检查业务入口无法调用到的代码及确定的冗余。不运行应用 |
| `dever test <root> [--filter ...] [--timeout-ms ...]` | 检查全部源码,实际执行所选测试断言(默认全部);不证明生产入口完整 |
| `dever run/build` | 通过与 check 相同的应用硬检查,再准备本地编译资源并执行或编译;成功不等于部署或性能验收 |
| AI 审查 | 业务含义、职责拆分、复用、硬编码与验证范围,不能替代程序检查 |
`check/run/build` 会拒绝这些 App/Domain 问题(C016):
- 接口、命令和后台任务等生产入口都调用不到的函数。
- 符合现有判定条件的私有空转发和 Model CRUD 包装。
- 生产入口可达、同一源码文件内契约和实现完全相同、没有副作用的私有函数。
- 生产入口可达的函数内,参数未变且前一次结果仍可复用的重复纯调用。直接复用已有结果;参数或结果被修改时不按这条拒绝。
这些规则不强迫合并有副作用、契约不同或跨文件的私有实现,也不能识别所有无用代码。
`check` 不准备 Worker 或运行资源,不生成原生程序,也不启动服务或数据库。`test` 只检查自己的入口和 Port fake,不套用生产应用硬检查;测试调用不能证明函数被实际业务使用,因此还要运行 `check`。
未使用赋值、部分多余条件、重复私有实现、重复纯调用和空转发还可能产生警告(W001–W005)。普通库和测试保留这些警告;应用满足上述条件时报 C016,同一源码证据的对应警告不重复显示。其他警告仍会显示,警告本身不使命令失败,但必须审查并处理本次引入的问题。编译器不能判断所有业务硬编码;skill 和 AGENTS 的要求也不自动变成编译错误。
按改动范围和用户授权选择检查。新模板可运行全部测试;已有大项目优先定向测试,不默认启动服务、接真实数据库或压测。未运行的必要检查应说明原因,不能报成通过。
已有 `dever.api` 时保留接口基线。需要比较新接口时,用 `dever api <root> --output <新文件>` 导出,不直接覆盖原文件。
源码放 module/,测试放 test/,部署配置放 config/,运行数据放 data/。生成程序从二进制同级 config/setting.json 读取配置。应用无需打包编译器;使用外部 Lib 时相关 runtime/依赖由 build 嵌入。只读 check 不证明真实数据库或部署验收。
## 核心与业务的区别
[源码仓库](https://git.dever.cc/shemic/dever) 中,`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。