停止与 NestJS 模块的斗争:认识 Rikta

发布: (2026年1月20日 GMT+8 00:45)
7 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容,我将按照要求保留源链接、格式和代码块,仅翻译正文部分。

模块问题

NestJS 模块解决了一个真实的问题:依赖边界。它们可以防止大型代码库陷入混乱,但也会带来代价。

每新增一个服务,你需要:

  1. 使用 @Injectable() 创建服务。
  2. 将它加入模块的 providers 数组。
  3. 如果其他模块需要,加入 exports 数组。
  4. 在所有使用它的地方导入该模块。

一个典型的 NestJS 模块如下所示:

// user.module.ts
@Module({
  imports: [
    DatabaseModule,
    ConfigModule,
    AuthModule,
    LoggingModule,
  ],
  controllers: [UserController],
  providers: [
    UserService,
    UserRepository,
    UserValidator,
    UserMapper,
  ],
  exports: [UserService, UserRepository],
})
export class UserModule {}

这会产生大量的样板代码——纯粹的配置开销会迅速增长。

一个中等规模的应用可能拥有 20+ 个模块,每个模块包含 5‑10 个 provider。你最终花在管理数组上的时间会超过编写业务逻辑的时间。

最糟糕的地方? 任意数组中的一个拼写错误就会破坏整个依赖图,调试可能需要数小时。

如果不需要模块会怎样?

Rikta 是一个基于 Fastify 的全新 TypeScript 框架。它保留了开发者喜爱的 NestJS 特性(装饰器、依赖注入、结构),并且完全去除了模块配置

  • 没有 imports 数组。
  • 没有 exports 数组。
  • 没有 providers 数组。

只需给类加上装饰器,一切即可工作。

// user.service.ts
import { Injectable } from '@riktajs/core';

@Injectable()
export class UserService {
  getUsers() {
    return ['Alice', 'Bob'];
  }
}
// user.controller.ts
import { Controller, Get, Autowired } from '@riktajs/core';
import { UserService } from './user.service';

@Controller('/users')
export class UserController {
  @Autowired()
  private userService!: UserService;

  @Get()
  getUsers() {
    return this.userService.getUsers();
  }
}

就是这样——没有模块文件,也无需注册。Rikta 在启动时会扫描你的代码并自动解析依赖。

零配置自动装配工作原理

Rikta 使用三种机制:

  1. 自动发现 – 启动时它会扫描项目中使用 @Controller()@Injectable() 装饰的类。它根据构造函数参数和 @Autowired() 装饰器构建依赖图。
  2. 全局提供者注册表 – 所有提供者都存放在同一个注册表中。任何可注入的类在应用的任何位置都可用;无需显式导出或导入。
  3. 依赖解析 – Rikta 读取 TypeScript 元数据(通过 reflect-metadata)来了解每个类的需求,按正确顺序创建实例,并自动注入它们。

在初始化期间会检测循环依赖,并给出明确的错误信息:

Error: Circular dependency detected: 
UserService -> AuthService -> UserService

没有晦涩的堆栈跟踪,也没有运行时的意外。

并排比较

在 NestJS 中创建新功能

// 1. Create the service
@Injectable()
export class PaymentService {
  constructor(private configService: ConfigService) {}
}

// 2. Create the module
@Module({
  imports: [ConfigModule],
  providers: [PaymentService],
  exports: [PaymentService],
})
export class PaymentModule {}

// 3. Import in app.module.ts
@Module({
  imports: [PaymentModule, /* ... */],
})
export class AppModule {}

// 4. Import in any module that needs it
@Module({
  imports: [PaymentModule],
  providers: [OrderService],
})
export class OrderModule {}

在 Rikta 中创建相同功能

@Injectable()
export class PaymentService {
  @Autowired()
  private configService!: ConfigService;
}

完成。

性能优势

Rikta 使用 Fastify 作为其 HTTP 层。Fastify 在基准测试中每秒处理高达 30,000 请求

官方基准结果:

指标Rikta 对比 NestJS
启动时间快 43 %
GET 请求快 41 %
POST 请求快 25 %
路由参数快 46 %

Rikta 只在原生 Fastify 上增加 2‑5 % 的开销,为您提供依赖注入和装饰器,同时不牺牲速度。

内置 Zod 验证

NestJS 依赖 class-validator 装饰器,这意味着你需要定义两次类型:一次用于 TypeScript,另一次用于验证。

Rikta 原生集成 Zod。只需定义一次模式,即可自动获得验证功能和 TypeScript 类型。

import { Controller, Post, Body, z } from '@riktajs/core';

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
  age: z.number().optional(),
});

@Controller('/users')
export class UserController {
  @Post()
  create(@Body(CreateUserSchema) user: z.infer<typeof CreateUserSchema>) {
    // 'user' is validated AND typed
    // Invalid requests return 400 automatically
    return { created: user };
  }
}

没有重复的类型定义,也不需要手动处理验证失败的错误。

何时使用 Rikta

Rikta 最适合:

  • 初创公司和 MVP,在开发速度至关重要的情况下。
  • 中小型团队(1‑15 名开发者),希望拥有干净、零配置的 DI 系统。

如果你喜欢 NestJS 的易用性,但厌倦了模块样板代码,试试 Rikta。

  • 微服务——每个服务保持专注。
  • 熟悉 NestJS 的开发者,希望减少样板代码。

考虑使用 NestJS 的情况:

  • 如果你的团队规模很大,需要严格的模块边界。
  • 你需要庞大的 NestJS 生态系统(特定适配器、插件)。
  • 你的组织要求明确的依赖文档。

入门指南

几秒钟内创建一个新项目:

npx @riktajs/cli new my-app
cd my-app
npm run dev

你的 API 运行在 http://localhost:3000

CLI 会生成一个完整的项目,包含:

  • 针对 Rikta 优化的 TypeScript 配置
  • 带有 REST 端点的示例控制器
  • 带有依赖注入的示例服务
  • 热重载开发服务器

资源

  • Documentation: (link)
  • GitHub: (link)
  • NPM: (link)

Rikta 使用 MIT 许可证,且为开源项目。


你尝试过 zero‑config 框架吗?对于模块化与自动装配的取舍,你有什么看法?请在评论中分享你的经验。

Back to Blog

相关文章

阅读更多 »

介绍 graphql-complexity-validation

✨ 特性 - 零运行时依赖 - 完全类型化的 TypeScript - 支持 fragments 与 inline fragments - 默认忽略 Introspection 适用于: - Apollo…