到现在为止,我们学习了,express结构,编写接口,mysql数据库读写数据,knex,prisma ORM结构,现在是时候把这些组合到一同,而且完成一个类似于Nestjs或许java的SpringBoot的架构真正的去开发我们的nodejs项目

MVC

MVC(Model-View-Controller)是一种常用的软件架构模式,用于规划和组织应用程序的代码。它将应用程序分为三个首要组件:模型(Model)、视图(View)和操控器(Controller),各自担任不同的责任。

  1. 模型(Model):模型表示应用程序的数据和事务逻辑。它担任处理数据的存储、检索、验证和更新等操作。模型通常包含与数据库、文件体系或外部服务进行交互的代码。
  2. 视图(View):视图担任将模型的数据以可视化的形式呈现给用户。它担任用户界面的展现,包含各种图形元素、页面布局和用户交互组件等。视图通常是根据模型的状况来动态生成和更新的。
  3. 操控器(Controller):操控器充当模型和视图之间的中间人,担任协调两者之间的交互。它接收用户输入(例如按钮点击、表单提交等),并根据输入更新模型的状况或调用相应的模型办法。操控器还可以根据模型的变化来更新视图的显现。

MVC 的首要方针是将应用程序的逻辑、数据和界面别离,以提高代码的可维护性、可扩展性和可重用性。经过将不同的责任分配给不同的组件,MVC 提供了一种明晰的结构,使开发人员能够更好地办理和修正应用程序的各个部分。

IoC操控回转和DI依靠注入

操控回转(Inversion of Control,IoC)和依靠注入(Dependency Injection,DI)是软件开发中常用的规划模式和技能,用于解耦和办理组件之间的依靠联系。虽然它们常常一同使用,但它们是不同的概念。

  1. 操控回转(IoC)是一种规划原则,它将组件的操控权从组件本身转移到外部容器。传统上,组件担任自己的创立和办理,而操控回转则将这个责任转给了一个外部的容器或结构。容器担任创立组件实例并办理它们的生命周期,组件只需声明自己所需的依靠联系,并经过容器获取这些依靠。这种回转的操控权使得组件更加松耦合、可测试和可维护。

  2. 依靠注入(DI)是完成操控回转的一种具体技能。它经过将组件的依靠联系从组件内部移动到外部容器来完成松耦合。组件不再担任创立或办理它所依靠的其他组件,而是经过结构函数、特点或办法参数等方式将依靠联系注入到组件中。依靠注入可以经过结构函数注入(Constructor Injection)、特点注入(Property Injection)或办法注入(Method Injection)等方式完成。

安装依靠

  1. inversify + reflect-metadata 完成依靠注入 官网

  2. 接口编写express 官网

  3. 衔接工具 inversify-express-utils 文档

  4. orm结构 prisma 官网

  5. dto class-validator + class-transformer 文档

项目架构

新建一个app文件夹

经过 prisma init --datasource-provider mysql 构建prisma项目 上一章讲过了

juejin.cn/post/733718…

目录结构

  • /src
    • /user
      • /controller.ts
      • /service.ts
      • /user.dto.ts
    • /post
      • /controller.ts
      • /service.ts
      • /post.dto.ts
    • /db
      • /index.ts
    • /prisma
      • /schema.prisma
  • main.ts
  • .env
  • tsconfig.json
  • package.json
  • README.md

代码编写

main.ts

import 'reflect-metadata'
import { InversifyExpressServer } from 'inversify-express-utils'
import { Container } from 'inversify'
import { UserController } from './src/user/controller'
import { UserService } from './src/user/service'
import express from 'express'
import { PrismaClient } from '@prisma/client'
import { PrismaDB } from './src/db'
const container = new Container() //Ioc搞个容器
/**
 * prisma依靠注入
 */
 //注入工厂封装db
container.bind<PrismaClient>('PrismaClient').toFactory(()=>{
    return () => {
        return new PrismaClient()
    }
})
container.bind(PrismaDB).toSelf()
/**
 * user模块
 */
container.bind(UserService).to(UserService) //添加到容器
container.bind(UserController).to(UserController) //添加到容器
/**
 * post模块
 */
const server = new InversifyExpressServer(container) //返回server
//中间件编写在这儿
server.setConfig(app => {
    app.use(express.json()) //接受json
})
const app = server.build() //app就是express
app.listen(3000, () => {
    console.log('http://localhost:3000')
})

src/user/controller.ts

import { controller, httpGet as GetMapping, httpPost as PostMapping } from 'inversify-express-utils'
import { inject } from 'inversify'
import { UserService } from './service'
import type { Request, Response } from 'express'
@controller('/user') //路由
export class UserController {
    constructor(
        @inject(UserService) private readonly userService: UserService, //依靠注入
    ) { }
    @GetMapping('/index') //get恳求
    public async getIndex(req: Request, res: Response) {
        console.log(req?.user.id)
        const info = await this.userService.getUserInfo()
        res.send(info)
    }
    @PostMapping('/create') //post恳求
    public async createUser(req: Request, res: Response) {
        const user = await this.userService.createUser(req.body)
        res.send(user)
    }
}

src/user/service.ts

import { injectable, inject } from 'inversify'
import { UserDto } from './user.dto'
import { plainToClass } from 'class-transformer' //dto验证
import { validate } from 'class-validator' //dto验证
import { PrismaDB } from '../db'
@injectable()
export class UserService {
    constructor(
        @inject(PrismaDB) private readonly PrismaDB: PrismaDB //依靠注入
    ) {
    }
    public async getUserInfo() {
        return await this.PrismaDB.prisma.user.findMany()
    }
    public async createUser(data: UserDto) {
        const user = plainToClass(UserDto, data)
        const errors = await validate(user)
        const dto = []
        if (errors.length) {
            errors.forEach(error => {
                Object.keys(error.constraints).forEach(key => {
                    dto.push({
                        [error.property]: error.constraints[key]
                    })
                })
            })
            return dto
        } else {
            const userInfo =  await this.PrismaDB.prisma.user.create({ data: user })
            return userInfo
        }
    }
}

src/user/user.dto.ts

import { IsNotEmpty, IsEmail } from 'class-validator'
import { Transform } from 'class-transformer'
export class UserDto {
    @IsNotEmpty({ message: '用户名必填' })
    @Transform(user => user.value.trim())
    name: string
    @IsNotEmpty({ message: '邮箱必填' })
    @IsEmail({},{message: '邮箱格局不正确'})
    @Transform(user => user.value.trim())
    email: string
}

src/db/index.ts

import { injectable, inject } from 'inversify'
import { PrismaClient } from '@prisma/client'
@injectable()
export class PrismaDB {
    prisma: PrismaClient
    constructor(@inject('PrismaClient') PrismaClient: () => PrismaClient) {
       this.prisma = PrismaClient()
    }
}

tsconig.json

支持装饰器和反射 翻开一下 严格模式封闭

"experimentalDecorators": true,
"emitDecoratorMetadata": true,    
"strict": false,