NestJS 模块化用户接口:controller/service 分层实战
Node 后端写到一定规模,所有路由和处理逻辑堆在一个文件里就难以维护。NestJS 借用了 Angular 的模块化思想,把功能按“模块”切分,每个模块里清晰分成控制器(收请求)、服务(写逻辑)、模块(做装配)。这篇文章用一个用户接口,把这套分层结构跑通,重点在“为什么分层、各层负责什么”。
一、为什么用模块化分层而不是全写在一起
小接口把所有逻辑塞一个文件,短期爽、长期痛:路由、校验、数据库操作混在一起,改一处怕动一片,单元测试也没法单独跑某块逻辑。NestJS 的套路是职责分离——控制器只管“收到请求、调服务、返结果”,业务逻辑放进服务,模块把两者和一个数据实体绑到一起。这样每块单一职责,可测、可读、可替换。
它底层默认基于 Express(也能换 Fastify),所以之前写的 Express 接口经验不浪费,只是多了一层组织规范。
二、模块、控制器、服务三层
一个用户模块由三件套组成:service 写业务逻辑,controller 写路由,module 把三者装配:
users/
users.service.ts # 业务逻辑
users.controller.ts # 路由入口
users.module.ts # 装配
users.service.ts 里放“怎么查用户”这类逻辑,不直接碰请求对象:
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
private users = [{ id: 1, name: '张三' }];
findOne(id: number) {
return this.users.find((u) => u.id === id);
}
}
@Injectable() 标记这是个可被注入的服务。数据这里用内存数组演示,真实项目把 users 换成数据库仓储即可,服务对外的方法签名不变。
三、控制器只管收请求
控制器用装饰器声明路由,把活儿委托给服务,自己不写业务:
import { Controller, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly service: UsersService) {}
@Get(':id')
findOne(@Param('id') id: string) {
return this.service.findOne(Number(id));
}
}
@Controller('users') 给整个控制器定前缀,@Get(':id') 绑定 GET /users/:id,@Param('id') 取路径参数。控制器构造函数里通过依赖注入拿到服务,不用自己 new。路由和逻辑分离后,控制器薄得像一层壳。
四、模块做装配
users.module.ts 把控制器和服务声明到一起,NestJS 据此建立依赖关系:
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
controllers 放路由类、providers 放服务类。模块之间也能互相 imports,把用户模块挂到根模块,接口就生效了。这种“功能内聚成一个模块、模块间按需引用”的结构,是项目变大后还能理清关系的关键。
五、几个容易踩的点
第一,忘了在 providers 里注册服务,控制器注入时会报依赖找不到——NestJS 的依赖是显式声明的,不注册就用不了。第二,业务逻辑写在控制器里会破坏分层,控制器应只做“接参数、调服务、返结果”。第三,服务里直接操作数据库时要处理空结果,控制器层决定返 404 还是 200,职责别乱。第四,全局异常建议用 NestJS 的过滤器统一处理,别在每个接口里手写 try/catch。
六、小结
NestJS 用“模块 + 控制器 + 服务”把 Node 后端按职责切开:控制器收请求、服务写逻辑、模块做装配,依赖注入把三者连起来。和之前 Express 平铺式、Gin handler 式比,它更适合中大型项目长期维护。理解分层与依赖注入,用户、订单、商品各成模块,项目再大结构也不乱。
- 点赞
- 收藏
- 关注作者
评论(0)