NestJS 模块化用户接口:controller/service 分层实战

举报
柠檬-夏日清爽 发表于 2026/10/15 11:13:08 2026/10/15
【摘要】 用 NestJS 写一个带模块化结构的用户接口,说明为什么分层(控制器/服务/模块)、依赖注入如何连接各层,以及 providers 注册等注意点。

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 式比,它更适合中大型项目长期维护。理解分层与依赖注入,用户、订单、商品各成模块,项目再大结构也不乱。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。