Skip to content

目录结构

ACE Framework 遵循"高内聚、低耦合"的分层原则。每个目录承担单一职责,可独立演进。以下以 task-api 示例项目为蓝本展示完整结构。

完整目录树

task-api/
├── cjpm.toml                  # 包配置(package、dependencies、sub-packages)
├── src/
│   ├── main.cj                # 程序入口,显式 import 触发自注册
│   ├── controller/            # HTTP 路由层(@Controller + @Get/@Post …)
│   │   ├── task_controller.cj
│   │   └── user_controller.cj
│   ├── service/               # 业务逻辑层(@Service / @Prototype)
│   │   ├── task_service.cj
│   │   └── user_service.cj
│   ├── domain/                # 领域模型 / 实体(@Entity、@Id、@Column)
│   │   ├── task.cj
│   │   └── user.cj
│   ├── dto/                   # 数据传输对象(请求体 / 响应体结构)
│   │   ├── create_task_dto.cj
│   │   └── task_response.cj
│   ├── middleware/            # 自定义中间件(@Middleware[order])
│   │   ├── auth_middleware.cj
│   │   └── logging_middleware.cj
│   └── exception/             # 全局异常处理(@Catch[ExceptionType])
│       ├── not_found_handler.cj
│       └── validation_handler.cj
└── config/
    ├── application.toml       # 基础配置(所有环境共用)
    ├── application-local.toml # 本地开发覆盖
    ├── application-dev.toml   # 开发环境覆盖
    ├── application-test.toml  # 测试环境覆盖
    └── application-prod.toml  # 生产环境覆盖

目录职责说明

目录职责典型注解
src/controller/接收 HTTP 请求,参数绑定,调用 Service,返回响应@Controller @Get @Post @PathParam @Body
src/service/核心业务逻辑,事务边界,调用 Repository/外部服务@Service @Transactional @Cacheable
src/domain/实体定义,与数据库表一一映射@Entity @Id @Column
src/dto/纯数据结构,用于请求体反序列化和响应体序列化无注解(普通 struct/class
src/middleware/横切关注点:认证、日志、限流等,按 order 排序挂载@Middleware[order]
src/exception/全局异常拦截,将异常映射为标准 HTTP 响应@Catch[ExceptionType]
config/多环境配置文件,TOML 格式,按环境叠加合并

cjpm.toml 结构说明

toml
[package]
name = "task_api"
version = "1.0.0"
cangjie-version = "1.1.0"

[dependencies]
ace_framework = { path = "../../ace-framework" }
ace_http      = { path = "../../ace-http" }

[target.aarch64-apple-darwin.bin-dependencies]
# stdx 预编译路径(本机 macOS 26 绕过,勿删)
path-option = "..."

ACE 的各能力模块(ace-web / ace-router / ace-bodyparser / ace-http / ace-framework)均作为独立成员发布在 workspace 下,task-api 只需在 [dependencies] 中声明所需层。cjpm 会自动处理传递依赖,无需手动列出 ace-web。

为什么 main.cj 必须显式 import 子包

ACE 的声明式自注册机制依赖顶层 let 变量的初始化器在程序启动时自动执行。以 @Service 为例,宏展开后会在源文件顶层生成:

cangjie
let __ace_reg_TaskService = Registry.register("TaskService") { TaskService() }

仓颉编译器只会编译被直接或间接 import 引用的包。若 main.cj 不导入 controllerservice 包,这些顶层初始化器将永远不会执行,容器中也就没有任何可注入的 Bean。

cangjie
// src/main.cj
package task_api

// 必须显式 import 各子包,否则 @Service/@Controller 的自注册不会触发
import task_api.controller.*
import task_api.service.*
import task_api.middleware.*
import task_api.exception.*

import ace_framework.*
import ace_http.*

main(): Int64 {
    let app = AceApplication.create()
    app.listen(8080)
    return 0
}

与 Spring/MidwayJS 的差异

Spring 通过运行时 classpath 扫描(反射)自动发现 Bean;MidwayJS 通过 TypeScript 装饰器元数据在启动时扫描。ACE 选择编译期宏 + 零反射路线——宏生成等价的显式注册代码,import 语句替代运行时扫描,在保持声明式体验的同时消除了反射开销。

常见错误

忘记 import 某个 controller 包后,路由注册不生效,但框架不会报错——容器中仅仅没有该 Controller 的 Bean。遇到 404 时,优先检查 main.cj 的 import 列表。

与 MidwayJS 目录对比

MidwayJSACE Framework说明
src/controller/src/controller/相同职责
src/service/src/service/相同职责
src/entity/src/domain/ACE 用 domain 体现领域驱动意图
src/dto/src/dto/相同职责
src/middleware/src/middleware/相同职责
src/filter/src/exception/ACE 用 @Catch 注解替代 Filter 概念
bootstrap.ts 扫描装饰器main.cj 显式 importACE 无运行时扫描,import 即注册
configuration.tsconfig/application.tomlACE 统一用 TOML 配置,无 JS 配置文件

基于 Apache-2.0 许可证发布