Skip to content

用 AI 编码代理开发 DDK 项目 ​

Claude Code、Codex 这类代理写代码很快,也很容易把分层写乱。DDK 生成的项目把约定写成代理会读的文件,把分层写成会失败的测试,再把失败结果整理成代理能照着改的报告,形成一个闭环。

生成的项目里有什么 ​

用 archetype 或 scripts/ddk.sh new 生成项目后,根目录会多出这些文件:

text
order-service/
├── AGENTS.md                   分层职责、编码约定、禁止事项、完成标准
├── CLAUDE.md                   @AGENTS.md,并提示优先使用 Skills
├── .claude/skills/
│   ├── ddk-add-aggregate/      新增聚合
│   ├── ddk-add-use-case/       新增用例
│   └── ddk-add-domain-event/   新增领域事件
└── src/test/java/.../ArchitectureTest.java
文件谁会读内容
AGENTS.mdCodex 等支持 AGENTS.md 的代理,也适合人读每个包放什么、不放什么;聚合、用例、持久化的写法;提交前必须通过 mvn verify,不许删改 ArchitectureTest
CLAUDE.mdClaude Code通过 @AGENTS.md 导入同一份约定,避免两份文件内容漂移
.claude/skills/*Claude Code(其他代理可以当检查清单读)按本项目包结构写好的分步说明,与示例项目 ddk-example-user 的真实写法一致

三层骨架生成同样的 AGENTS.md 与 CLAUDE.md,Skill 合并为一个 ddk-add-feature。

反馈回路 ​

text
代理按 AGENTS.md / Skill 写代码
        │
        ▼
    mvn verify ──── 通过 ────► 完成
        │
      失败
        ▼
target/archguard/violations.md    哪条规则 · 哪个类 · 怎么改
        │
        └──► 代理按 hint 修改代码(不删、不放宽规则)──► 再次 mvn verify

关键在最后一步。ArchitectureTest 使用 ArchGuard.check:它会执行全部规则,把每条违规连同所在类和修复建议写进报告与断言消息里。代理不需要理解 ArchUnit 的规则描述,照着 hint 改即可。报告格式见 ArchGuard 架构守卫。

一次典型的协作 ​

在生成的四层项目里,对代理说:

text
新增「订单」聚合:订单有待支付、已支付、已取消三种状态,只有待支付的订单能取消。
再加一个取消订单的接口,取消后发出订单已取消事件。

对应 ddk-add-aggregate、ddk-add-use-case、ddk-add-domain-event 三个 Skill,工作会按这样的顺序展开:

  1. 在 domain.model 写 OrderId 与 Order,状态变更守卫写在 Order.cancel() 里;
  2. 在 domain.acl 声明 OrderRepository,在 infrastructure 写 PO、Mapper、两个方向的转换器和仓储实现;
  3. 在 application 写命令、响应与应用服务方法,在 adapter 写请求对象与接口;
  4. 登记 OrderCancelledEvent,在 application.handler 用 AFTER_COMMIT 订阅;
  5. 运行 mvn verify。如果某一步把 OrderPO 引进了领域层,报告会指出 Order 违反了哪两条规则,代理改为通过仓储端口访问后重新验证。

一次真实的会话记录 ​

下面是一次实际运行的记录,没有修饰。环境:用 DDK v0.3.0 的四层 archetype 生成 order-service,由 Claude Code 的子代理(Opus 5.5)在项目里完成上一节的任务,提示词就是那两句话,另外只要求它保留每一次 mvn verify 的输出。

text
读取   AGENTS.md、三个 Skill 的 SKILL.md、pom.xml、ArchitectureTest,以及 AGENTS.md 指向的用户示例
领域   domain/model      OrderId、OrderStatus、Order(create / restore / pay / cancel,无 setter)
       domain/error      OrderError
       domain/event      OrderCreatedEvent、OrderCancelledEvent
       domain/acl        OrderRepository extends GenericRepository<Order, OrderId>
基础   infrastructure    OrderPO、OrderMapper、两个方向的转换器、OrderRepositoryImpl、schema.sql
应用   application       CancelOrderCommand、OrderResponse、OrderService.cancel、OrderEventHandler(AFTER_COMMIT)
接口   adapter           CancelOrderRequest、OrderController:POST /orders/{id}/cancel
测试   OrderTest(7 个)、OrderApiTest(6 个)
验证   mvn verify        第 1 次即通过
text
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 -- in com.example.orderservice.adapter.controller.OrderApiTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 -- in com.example.orderservice.ApplicationTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 -- in com.example.orderservice.ArchitectureTest
[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0 -- in com.example.orderservice.domain.model.OrderTest
[INFO] Tests run: 15, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

几点观察:

  1. 一次通过,没有触发反馈回路。 新增 21 个文件,没有改动任何已有文件,target/archguard/violations.md 的内容是 No architecture violations.。这次起作用的是 AGENTS.md 和 Skill 里写明的分层约定,架构测试只是确认了结果。
  2. 代理补了需求里没有的东西,并且都说明了。 订单金额与取消原因字段、OrderCreatedEvent、让「已支付」状态可达的 Order.pay();取消原因按 Skill 里的示例定为必填。这些是需要人确认的判断,不是架构问题,护栏管不到。
  3. 样本只有一次。 一个小任务一次通过,不能说明代理在更大的改动里也不会越界。

为了看到反馈回路的实际输出,我们在这次会话之后手工往 Order 里加了一个接收 OrderPO 的方法(这一步不是代理做的):

java
public static Order fromPo(OrderPO po) { ... }   // 领域层引用了持久化对象

mvn verify 随即失败,violations.md 报出 12 条违规,分属两条规则(节选):

text
# ArchGuard report

12 violation(s). Fix the code; do not delete or relax the rules.

## DOMAIN_MUST_NOT_DEPEND_ON_OUTER_LAYERS

**How to fix**: 领域层不得引用外层类型:需要的能力在 domain.acl 定义端口并由 infrastructure 实现,需要的数据以参数或值对象传入。

- `com.example.orderservice.domain.model.Order`: Method <...Order.fromPo(...OrderPO)> has parameter of type <...infrastructure.orm.po.OrderPO> in (Order.java:0)
- `com.example.orderservice.domain.model.Order`: Method <...Order.fromPo(...OrderPO)> calls method <...OrderPO.getId()> in (Order.java:85)

另一条是 LAYERED_ARCHITECTURE_RULE,指向同一个方法。代理越界时读到的就是这份报告:哪条规则、哪个类、哪一行、怎么改。

为什么这样设计 ​

  • 约定写成代理会读的文件:口头约定和团队 Wiki 不会出现在代理的上下文里,AGENTS.md 会。
  • 规则写成会失败的测试:代理会绕过建议,但绕不过红色的构建;同时明确写出「不许删改 ArchitectureTest」。
  • 失败结果写成可执行的建议:ArchUnit 的原始描述面向规则作者,报告里的 hint 面向要改代码的人或代理。
  • 一份约定,多个代理:AGENTS.md 是 Codex 等工具的约定入口,CLAUDE.md 只做导入,Skills 是 Claude Code 的增强。

让代理调用你的服务 ​

上面讲的是代理编写代码。反过来,代理也可以调用服务:引入 ddk-mcp-starter 后,适配层里标了 @McpTool 的方法会成为 MCP 工具。参数先经过 Bean Validation,业务异常以错误码返回,工具只能经由应用服务访问领域,由 MCP_TOOLS_MUST_RESIDE_IN_ADAPTER 规则约束。详见 MCP 工具。

DDK 仓库本身 ​

DDK 仓库根目录同样有 AGENTS.md 与 CLAUDE.md,写明构建门禁(Spotless、JaCoCo、NullAway、ArchUnit)、starter 契约、公开与 internal 包的划分和提交规范,方便用 AI 代理参与 DDK 的开发。

文章以 CC BY-NC-SA 4.0 授权 · 代码片段以 MIT 授权