MVC
MVC(Model-View-Controller)是一种常见的软件架构模式,用于设计和组织应用程序的代码
它将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller),它们各自承担不同的职责
- 模型
- 表示应用程序的数据和业务逻辑
- 负责处理数据的存储、检索、验证和更新等操作
- 通常包含与数据库、文件系统或外部服务进行交互的代码
- 视图
- 将模型的数据以可视化的形式呈现给用户
- 负责用户界面的展示,包括各种图形元素、页面布局和用户交互组件等
- 通常根据模型的状态动态生成和更新
- 控制器
- 充当模型和视图之间的中间人,负责协调两者之间的交互
- 接收用户输入(例如按钮点击、表单提交等),并根据输入更新模型的状态或调用相应的模型方法
- 可以根据模型的变化来更新视图的显示
MVC将应用程序的逻辑、数据和界面分离,提高了代码的可维护性、可扩展性和可重用性
MVC的清晰结构将不同的职责分配给不同的组件,使开发人员能够更好地管理和修改应用程序的各个部分
IoC和DI
IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)是软件开发中常用的设计模式和技术,用于解耦和管理组件之间的依赖关系
虽然经常一起使用,但它们是不同的概念
控制反转
控制反转是一种设计原则,它将组件的控制权从组件自身转移到外部容器
传统上,组件负责自己的创建和管理,而控制反转则将这个责任转给了一个外部的容器或框架
容器负责创建组件实例并管理它们的生命周期,组件只需声明自己所需的依赖关系,并通过容器获取这些依赖
这种控制权的反转使得组件更加松耦合、可测试、可维护
依赖注入
依赖注入是实现控制反转的一种具体技术,它通过将组件的依赖关系从组件内部移动到外部容器来实现松耦合
组件不再负责创建或管理它所依赖的其他组件,而是通过构造函数、属性或方法参数等方式将依赖关系注入到组件中
可以通过构造函数注入(Constructor Injection)、属性注入(Property Injection)或方法注入(Method Injection)等方式实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class A { constructor (b) { this.b = b } }
class B { constructor () {} }
class Container { constructor () { let b = new B() new A(b) } }
|
MVC项目
安装依赖
构建出这样一种项目结构
基本结构
需要在tsconfig.ts中开启装饰器语法和反射、关闭严格模式
1 2 3
| "experimentalDecorators": true, "emitDecoratorMetadata": true, "strict": false,
|
main.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import 'reflect-metadata' import { InversifyExpressServer } from 'inversify-express-utils' import { Container } from 'inversify' import express from 'express' import { User } from './src/user/controller' import { UserService } from './src/user/service'
const container = new Container()
container.bind(User).to(User) container.bind(UserService).to(UserService)
const server = new InversifyExpressServer(container)
server.setConfig(app => { app.use(express.json()) })
const app = server.build()
app.listen(11451, ()=> { console.log('server on 11451') })
|
src/user/controller.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
import { controller, httpGet as Get, httpPost as Post } from "inversify-express-utils"
import { UserService } from "./service"
import { inject } from "inversify"
import type { Request, Response } from "express"
@controller('/user') export class User {
constructor(@inject(UserService) private readonly userService: UserService) { }
@Get('/index') public getIndex(req: Request, res: Response) { let result = this.userService.getList() res.send(result) }
@Post('/create') public createUser(req: Request, res: Response) { let result = this.userService.createUser() res.send(result) } }
|
src/user/service.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
import { injectable } from "inversify"
@injectable() export class UserService { public getList() { return { list: ['yajue', 'kmr', 'mur'] } }
public createUser() { return { status: 'success' } } }
|
封装Prisma
以依赖注入的方式封装Prisma,让使用更便捷
main.ts中加入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { PrismaClient } from '@prisma/client' import { PrismaDB } from './src/db'
container.bind<PrismaClient>(PrismaClient).toFactory(()=> { return ()=> { return new PrismaClient() } })
container.bind(PrismaDB).to(PrismaDB)
|
src/db/index.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
import { injectable, inject } from "inversify" import {PrismaClient} from '@prisma/client'
@injectable() export class PrismaDB { prisma: PrismaClient
constructor (@inject('PrismaClient') private readonly prismaClient: ()=>PrismaClient) { this.prisma = this.prismaClient() } }
|
src/user/controller.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
import { controller, httpGet as Get, httpPost as Post } from "inversify-express-utils"
import { UserService } from "./service"
import { inject } from "inversify"
import type { Request, Response } from "express"
@controller('/user') export class User {
constructor(@inject(UserService) private readonly userService: UserService) { }
@Get('/index') public async getIndex(req: Request, res: Response) { let result = await this.userService.getList() res.send(result) }
@Post('/create') public async createUser(req: Request, res: Response) { let result = await this.userService.createUser(req.body) res.send(result) } }
|
src/user/service.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { injectable, inject } from "inversify" import { PrismaDB } from "../db"
@injectable() export class UserService {
constructor(@inject(PrismaDB) private readonly PrismaDB:PrismaDB) { }
public async getList() { return this.PrismaDB.prisma.user.findMany() }
public async createUser(user: any) { return await this.PrismaDB.prisma.user.create({ data: user }) } }
|
DTO
DTO层用于验证传递对象的合规性
src/user/user.dto.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { IsNotEmpty, IsEmail } from 'class-validator' import { Transform } from 'class-transformer'
export class UserDto { @IsNotEmpty({message: 'name is required'}) @Transform(({value})=> value.trim()) name: string
@IsNotEmpty({message: 'email is required'}) @IsEmail({}, {message: 'email must be formed'}) email: string }
|
在接收数据的地方进行验证,比如src/user/service.ts的createUser:
1 2 3 4 5 6 7 8 9 10 11 12
| public async createUser(user: UserDto) { const userDto = plainToClass(UserDto, user) const errors = await validate(userDto) if(errors.length) { return errors } else { return await this.PrismaDB.prisma.user.create({ data: user }) } }
|