目录结构

当你创建一个新的 AdonisJS 应用时,它会带有一套精心设计的默认目录结构,旨在让项目保持整洁、可预期且易于重构。

这套结构反映了适用于大多数项目的约定,但 AdonisJS 并不会将你锁死在其中。你可以自由地重新组织文件与目录,以契合团队的协作方式。

根据你选择的入门套件,某些文件或目录可能有所不同。例如,Inertia 入门套件包含一个顶层的 inertia 目录,而 Hypermedia 入门套件则不含此目录。

概述

下面是一个新建的 AdonisJS 项目大致的样子。

├── app
├── bin
├── config
├── database
├── resources
├── start
├── tests
├── ace.js
├── adonisrc.ts
├── eslint.config.js
├── package-lock.json
├── package.json
├── tsconfig.json
└── vite.config.ts

API 入门套件是一个由 Turborepo 管理的 monorepo,包含两个 workspace:

├── apps
   ├── backend           # AdonisJS 应用
   ├── app
   ├── bin
   ├── config
   ├── database
   ├── start
   ├── tests
   ├── ace.js
   ├── adonisrc.ts
   ├── package.json
   └── tsconfig.json
   └── frontend          # 你的前端应用
       ├── package.json
       └── ...
├── package.json          # 根 package.json,定义 workspaces
├── package-lock.json
└── turbo.json            # Turborepo 流水线配置

apps/backend 目录包含标准的 AdonisJS 应用——下面所有小节(app/config/start/ 等)都适用于该目录。apps/frontend 目录则是你搭建前端框架(TanStack Start、Next.js、Nuxt 等)的地方。

根目录的 package.json 定义了 workspaces:

{
  "workspaces": ["apps/*"]
}

turbo.json 配置了 devbuild 等脚本以跨两个应用运行。当你从项目根目录运行 npm run dev 时,Turborepo 会并行启动后端与前端开发服务器。

每个目录和文件都有特定用途。下面各小节将解释每一项的作用,以及你通常会在其中找到什么。

app/

app 目录用于组织应用领域逻辑相关的代码。例如控制器、模型、邮件、中间件等都位于 app 目录下。

你可以自由地在 app 目录下创建额外的文件夹,以更好地组织代码库。

├── app
   ├── controllers
   ├── exceptions
   ├── mails
   ├── middleware
   ├── models
   ├── transformers
   └── validators

bin/

bin 目录包含用于在不同环境中启动 AdonisJS 应用的入口文件。

  • console.ts 文件使用 Ace 命令行框架来执行 CLI 命令。
  • server.ts 文件在 Web 环境中启动应用以监听 HTTP 请求。
  • test.ts 文件用于为测试环境引导启动应用。

除非你想定制应用在特定上下文中的启动方式,否则通常无需修改这些文件。

├── bin
   ├── console.ts
   ├── server.ts
   └── test.ts

config/

所有应用级与第三方配置文件都存放在 config 目录下。你也可以将仅限本应用使用的配置存放在该目录中。

├── config
   ├── app.ts
   ├── auth.ts
   ├── bodyparser.ts
   ├── database.ts
   ├── hash.ts
   ├── limiter.ts
   ├── logger.ts
   ├── mail.ts
   ├── session.ts
   ├── shield.ts
   ├── static.ts
   └── vite.ts

database/

database 目录存放与数据库层相关的产物。默认情况下,AdonisJS 随附已为 SQLite 配置好的 Lucid ORM;切换数据库无需重新组织此文件夹。

  • migrations/ - 带版本的 schema 变更
  • seeders/ - 用于插入初始或测试数据的脚本
  • factories/ - 在测试或种子文件中生成模型实例的蓝图
  • schema.ts - 模型所使用的自动生成数据库 schema
  • schema_rules.ts - 验证器所使用的自动生成 schema 规则

另请参阅: Lucid 文档

database/
  ├── migrations/
  ├── seeders/
  ├── factories/
  ├── schema.ts
  └── schema_rules.ts

providers/

providers 目录用于存放应用所使用的 服务提供者 。你可以使用 node ace make:provider 命令创建新的提供者。

├── providers
  └── app_provider.ts

public/

public 目录包含原始的静态资源,这些资源无需任何编译步骤即可直接提供给浏览器。该目录中的文件可通过 http://localhost:3333/public/<file-name> 这样的 URL 经 HTTP 公开访问。

Note

API 入门套件不包含 public 目录,因为后端只返回 JSON 响应,不提供静态资源。

存放于该目录中的典型文件包括:

  • 网站图标 (favicon.ico)
  • 爬虫规则文件 (robots.txt)
  • 静态图片 (logo.png, social-banner.jpg)
  • 可下载资源 (manual.pdf)

resources/

resources 目录存放 Edge 模板,以及尚未编译的前端资源(如 CSS 与 JavaScript 文件)。

Note

API 入门套件不包含 resources 目录,因为后端只返回 JSON 响应,不渲染 HTML 模板。

对于使用 Inertia(配合 Vue 或 React)的应用,前端代码保存在 inertia 目录中,而 resources 目录仅包含根 Edge 模板。你可以把这个根模板理解为包含了前端应用 HTML 外壳的 index.html 文件。

├── resources
   ├── css
   ├── js
   └── views
       ├── home.edge
├── resources
   └── views
       └── inertia_layout.edge

inertia/

inertia 目录仅存在于使用 Inertia 入门套件的项目中。它代表一个包含前端源代码的子应用,包括 React 或 Vue 组件、页面以及配套的工具函数。

  • pages/ - 存放用 React 或 Vue 编写的 Inertia 页面。
  • app.(tsx|vue) - 客户端应用的主入口。
  • ssr.(tsx|vue) - 服务端渲染(SSR)的入口。
  • tsconfig.json - 前端代码库的 TypeScript 配置文件。其默认值针对浏览器环境、JSX/TSX 语法以及基于 Vite 的构建进行了优化。

你可以自由创建 components/layouts/utils/ 等额外的子文件夹来组织前端代码。

├── inertia
   ├── css
   ├── layouts
   ├── pages
   └── home.tsx
   ├── app.tsx
   ├── ssr.tsx
   ├── tsconfig.json
   └── types.ts

前端与后端的清晰分离

AdonisJS 在后端与前端之间保持着清晰的边界。你绝不应该将后端代码(如模型、服务或转换器)导入到前端应用中。

在实践中,你的前端通过 HTTP 请求与后端通信,并接收纯 JSON 数据。AdonisJS 鼓励你显式地建模这一现实:数据通过 API 响应来获取和转换,而不是被隐藏在共享抽象的背后。

共享类型

前端仍然可以依赖 AdonisJS 自动生成的共享 TypeScript 类型。这些类型存放在 .adonisjs/client 目录中,包含路由、props 以及其他共享契约的类型定义。

这种方式让前端代码在保持强类型的同时,又不破坏客户端与服务端之间的分离。

Tip

你应该将 .adonisjs 目录提交到版本控制中。框架依赖这些生成的文件来进行导入解析(例如 #generated/controllers)和 TypeScript 类型检查。缺少它们,生产构建和 CI 流水线都会失败。

我们建议阅读 类型生成文档 ,了解 AdonisJS 是如何创建共享类型的。

start/

start 目录包含你希望在应用启动生命周期中导入的文件。例如,用于注册路由和定义事件监听器的文件应放在此目录中。

├── start
   ├── env.ts
   ├── kernel.ts
   └── routes.ts

AdonisJS 不会自动导入 start 目录中的文件。它只是作为一个约定来归类相似的文件。我们建议阅读 预加载文件应用启动生命周期 ,以便更好地理解哪些文件应当保留在 start 目录下。

tests/

tests 目录包含自动化测试。AdonisJS 使用 Japa 测试运行器。

测试组织在特定的子文件夹中。运行 unit(单元)测试时,AdonisJS 会引导启动应用,但不会启动 HTTP 服务器;而在 functional(功能)测试中,我们会启动 HTTP 服务器与 Vite 开发服务器。

另请参阅: 测试文档

├── tests
   ├── unit
   ├── functional
   └── bootstrap.ts

tmp/

应用生成的临时文件存放在 tmp 目录中。例如,这些可能是用户上传的文件(开发期间生成),或写入磁盘的日志。

tmp 目录必须被 .gitignore 规则忽略,你也不应将其复制到生产服务器上。

ace.js

ace.js 文件是执行 Ace 命令的入口。该文件配置了一个 JIT TypeScript 编译器,然后导入 bin/console.ts 文件。

adonisrc.ts

adonisrc.ts 是项目的清单文件。它告诉 AdonisJS 如何发现并加载应用的各个部分,并包含以下配置:

  • 目录别名(对应 appstart 等)
  • 预加载文件
  • 已注册的提供者与服务命令
  • Assembler 钩子

另请参阅: AdonisRC 文件参考

eslint.config.js

该文件为项目配置 ESLint。其默认规则针对 TypeScript 与 AdonisJS 约定进行了调优。你可以修改或扩展此配置以契合团队的风格。

package.jsonpackage-lock.json

  • package.json - 项目元数据、脚本与依赖声明。
  • package-lock.json - 锁定确切的依赖版本,以保证安装的一致性。

tsconfig.json

tsconfig.json 控制 TypeScript 编译器选项、模块解析与路径别名。所提供的配置同时适用于开发与生产构建。不过,你可以调整编译器设置以匹配特定的工作流。

Note

AdonisJS 依赖 experimentalDecorators 来支持依赖注入、Ace 命令以及 Lucid ORM。

以下配置选项是 AdonisJS 内部正常运作所必需的。

{
  "compilerOptions": {
    "module": "NodeNext",
    "isolatedModules": true,
    "declaration": false,
    "outDir": "./build",
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipLibCheck": true
  },
  "include": ["**/*", ".adonisjs/server/**/*"]
}
{
  "compilerOptions": {
    "module": "NodeNext",
    "isolatedModules": true,
    "declaration": false,
    "outDir": "./build",
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipLibCheck": true
  },
  "references": [
    {
      "path": "./tsconfig.inertia.json"
    }
  ],
  "include": ["**/*", ".adonisjs/server/**/*"]
}

vite.config.ts

当项目使用 Vite(Edge 或 Inertia 入门套件)时,vite.config.ts 定义了前端资源如何编译与提供。如果你需要特殊的打包行为,可在此自定义入口、别名与插件设置。