Skip to content

Node.js MVC + IoC/DI 架构

将 Express + Prisma + inversify 组合,实现类似 NestJS/SpringBoot 的分层架构。

核心概念

概念说明
MVCModel(数据+业务逻辑)、View(视图)、Controller(协调层)三层分离
IoC控制反转 — 组件创建和管理权交给外部容器,组件只声明依赖
DI依赖注入 — IoC 的具体实现方式:通过构造函数/属性注入依赖

技术栈

bash
npm install inversify reflect-metadata inversify-express-utils
npm install prisma class-validator class-transformer
作用
inversifyIoC/DI 容器
inversify-express-utilsExpress + inversify 集成(@controller / @httpGet 等装饰器)
reflect-metadataTypeScript 装饰器元数据支持
class-validator + class-transformerDTO 参数校验

项目结构

/src
  /user
    controller.ts    # 路由层 — 只处理请求/响应
    service.ts       # 业务逻辑层 — 不碰 req/res
    user.dto.ts      # 数据传输对象 — 校验规则
  /post/
    controller.ts
    service.ts
    post.dto.ts
  /db
    index.ts         # Prisma 封装
  /prisma
    schema.prisma
main.ts              # 入口 — IoC 容器组装

依赖注入流程

main.ts — IoC 容器装配

ts
import 'reflect-metadata'
import { Container } from 'inversify'
import { InversifyExpressServer } from 'inversify-express-utils'
import { UserController } from './src/user/controller'
import { UserService } from './src/user/service'

const container = new Container()

// 注入 Prisma(工厂模式)
container.bind<PrismaClient>('PrismaClient').toFactory(() => () => new PrismaClient())
container.bind(PrismaDB).toSelf()

// 注入 User 模块
container.bind(UserService).to(UserService)
container.bind(UserController).to(UserController)

// 创建 Express 服务
const server = new InversifyExpressServer(container)
server.setConfig(app => app.use(express.json()))
const app = server.build()
app.listen(3000)

controller.ts — 路由 + 依赖注入

ts
import { controller, httpGet, httpPost } from 'inversify-express-utils'
import { inject } from 'inversify'
import { UserService } from './service'

@controller('/user')
export class UserController {
  constructor(
    @inject(UserService) private readonly userService: UserService  // DI
  ) {}

  @httpGet('/index')
  async getIndex(req, res) {
    res.send(await this.userService.getUserInfo())
  }

  @httpPost('/create')
  async createUser(req, res) {
    res.send(await this.userService.createUser(req.body))
  }
}

service.ts — 业务逻辑 + DTO 校验

ts
import { injectable, inject } from 'inversify'
import { validate } from 'class-validator'
import { plainToClass } from 'class-transformer'

@injectable()
export class UserService {
  constructor(@inject(PrismaDB) private prismaDB: PrismaDB) {}

  async getUserInfo() {
    return this.prismaDB.prisma.user.findMany()
  }

  async createUser(data: UserDto) {
    const user = plainToClass(UserDto, data)      // 数据转换
    const errors = await validate(user)            // 校验
    if (errors.length) return errors
    return this.prismaDB.prisma.user.create({ data: user })
  }
}

user.dto.ts — 校验规则

ts
import { IsString, IsEmail, Length } from 'class-validator'

export class UserDto {
  @IsString()
  @Length(2, 20)
  name: string

  @IsEmail()
  email: string
}

分层对比

main.ts          → IoC 容器(装配 Bean)
controller.ts    → 路由 + 请求响应处理(无业务逻辑)
service.ts       → 业务逻辑 + DTO 校验
db/index.ts      → Prisma 数据库封装
prisma/schema    → 数据模型定义