概述
- 在 NestJS 项目中实现多数据库对接,尤其是结合 TypeORM、Prisma 和 Mongoose 三种不同类型的 OM(对象关系映射)库,来应对多租户架构下的数据隔离与灵活切换需求
- 在现代企业级应用开发中,项目的模型层往往需要根据具体的业务场景来选择合适的数据库与对应的 OM 库。
- 在大多数情况下,不会在同一个项目中同时使用三种数据库,而是根据应用场景来决定是否需要支持多数据库连接
- 只有在项目中确实需要对接多种数据库时,才需要使用我们所讲的最小可行性方案
- 这个方案的核心思想是通过分层来统一接口,再根据不同的数据库类型实现具体的操作逻辑
- 这样,我们就不需要每种数据库都 inject 一次服务,通过统一的服务内部租户身份来进行区分,相当于外观(门面)模式 + 抽象工厂模式 + 策略模式
多租户与多数据库架构下的挑战与优化
- 在多租户系统中,数据隔离是一个核心问题。传统的单库架构无法满足多租户场景下的数据隔离与性能要求。因此,我们需要考虑多库架构(即每个租户拥有独立数据库)
- 然而,多库架构也带来了如下挑战
- 系统复杂度提升: 多库意味着更多的数据库连接、配置管理与数据迁移逻辑
- 维护成本上升: 数据库数量增加,对运维和监控系统提出了更高要求
- 数据同步问题: 跨库数据同步、一致性保障、事务处理等都需要额外设计
- 为解决这些问题,我们在项目中设计了一套基于多 ORM 库的动态数据库连接机制:
- 数据库连接信息可以从本地配置文件读取
- 也可以通过远程服务动态获取(如根据不同租户 ID 获取其数据库连接信息)
- 支持不同类型 OM 库(如 TypeORM、Prisma、Mongoose)的动态切换
ORM 库的适用场景与选型建议
- 在选择 ORM 库时,我们需要根据数据库类型与业务需求进行合理选择:
- TypeORM:更适合关系型数据库(如 MySQL、PostgreSQL),对非关系型数据库(如 MongoDB)支持有限
- Prisma:支持关系型与非关系型数据库,但版本更新频繁,社区支持不如 TypeORM 稳定
- Mongoose:专为 MongoDB 设计,功能强大,是操作 MongoDB 的首选 OM 库
- 因此,在项目中若需要同时对接多种数据库类型,可以采用如下组合:
- 对关系型数据库使用 TypeORM 或 Prisma
- 对 MongoDB 使用 Mongoose
- 项目中使用了 Prisma、TypeORM 和 Mongoose 三种 OM 库
- 这种组合适用于极端复杂的业务场景(如本地与云端数据同步、多种数据库共存等)
需求与架构设计
- 在设计之前,先了解需求:
-
- 支持 prisma, typeorm, mongoose 三种类型的库
-
- 支持mysql, postgresql, mongodb 数据库
-
- 租户标识
- prisma 库
- prisma1 -> mysql 数据库
- prisma2 -> postgresql 数据库
- typeorm 数据库有
- typeorm1 -> mysql1 数据库
- typeorm2 -> mysql2 数据库
- typeorm3 -> postgresql 数据库
- mongoose
- mongoose1 -> mongodb1 数据库
- mongoose2 -> mongodb2 数据库
- prisma 库
- 租户标识
-
- 请按实际业务场景来设计,上面仅作为演示需求,上面表达的是多租户和多数据库的需求
- 目录设计 (只列举核心目录)
src ├── app.module.ts ├── app.controller.ts ├── main.ts ├── ormconfig.ts ├── package.json ├── database │ ├── database.constants.ts │ ├── database.interfaces.ts │ ├── database.module.ts │ ├── database.utils.ts │ ├── prisma │ │ ├── prisma-common.module.ts │ │ ├── prisma-config.service.ts │ │ ├── prisma-core.module.ts │ │ ├── prisma-options.interfaces.ts │ │ ├── prisma-constants.ts │ │ ├── prisma.module.ts │ │ └── prisma.utils.ts │ ├── typeorm │ │ ├── typeorm-common.module.ts │ │ ├── typeorm-config.service.ts │ │ ├── typeorm.constants.ts │ │ └── typeorm.provider.ts │ ├── mongoose │ │ ├── mongoose-common.module.ts │ │ ├── mongoose-config.service.ts │ │ ├── mongoose-core.module.ts │ │ ├── mongoose.constants.ts │ │ ├── mongoose.interfaces.ts │ │ ├── mongoose.module.ts │ │ ├── mongoose.providers.ts │ │ └── mongoose.utils.ts ├── user │ └── repositories │ │ ├── repository.prisma.ts │ │ ├── repository.typeorm.ts │ │ └── repository.mongoose.ts │ ├── user.controller.ts │ ├── user.entity.ts │ ├── user.interfaces.ts │ ├── user.module.ts │ ├── user.repository.ts │ └── user.schema.ts - 服务启动的 compose.yaml 文件此处不再提供,因为部署时的服务和相关环境都是不一样的
- 只提供下面的配置上的地址,仅作演示
租户环境变量.env设计
# prisma 租户1/2
DATABASE_URL="mysql://root:123456_mysql@localhost:13306/testdb" # 这个需要留着, 生成客户端的时候需要此类格式
T_PRISMA1_DATABASE_URL="mysql://root:123456_mysql@localhost:13306/testdb"
T_PRISMA2_DATABASE_URL="postgresql://pguser:123456_postgresql@localhost:15432/testdb"
# typeorm 租户1 配置
T_TYPEORM1_DB_TYPE=mysql
T_TYPEORM1_DB_HOST=localhost
T_TYPEORM1_DB_PORT=13306
T_TYPEORM1_DB_USERNAME=root
T_TYPEORM1_DB_PASSWORD=123456_mysql
T_TYPEORM1_DB_DATABASE=testdb
T_TYPEORM1_DB_AUTOLOAD=true
T_TYPEORM1_DB_SYNC=true
# typeorm 租户2 配置
T_TYPEORM2_DB_TYPE=mysql
T_TYPEORM2_DB_HOST=localhost
T_TYPEORM2_DB_PORT=13307
T_TYPEORM2_DB_USERNAME=root
T_TYPEORM2_DB_PASSWORD=123456_mysql
T_TYPEORM2_DB_DATABASE=testdb
T_TYPEORM2_DB_AUTOLOAD=true
T_TYPEORM2_DB_SYNC=true
# typeorm 租户3 配置
T_TYPEORM3_DB_TYPE=postgres
T_TYPEORM3_DB_HOST=localhost
T_TYPEORM3_DB_PORT=15432
T_TYPEORM3_DB_USERNAME=pguser
T_TYPEORM3_DB_PASSWORD=123456_postgresql
T_TYPEORM3_DB_DATABASE=testdb
T_TYPEORM3_DB_AUTOLOAD=true
T_TYPEORM3_DB_SYNC=true
# mongoose 租户1/2
T_MONGOOSE1_MONGODB_URI="mongodb://root:123456_mongodb@localhost:27018/nestmongodb"
T_MONGOOSE2_MONGODB_URI="mongodb://root:123456_mongodb@localhost:27019/nestmongodb"
根目录下 app.module.ts 模块设计
import {
Module } from '@nestjs/common';
import {
AppController } from './app.controller';
import {
ConfigModule } from '@nestjs/config';
import {
DatabaseModule } from './database/database.module';
import {
UserModule } from './user/user.module';
@Module({
imports: [
ConfigModule.forRoot({
// 配置环境变量模块
envFilePath: '.env', // 指定环境变量文件路径
isGlobal: true, // 全局可用
}),
DatabaseModule,
UserModule,
],
controllers: [AppController]
})
export class AppModule {
}
这里,可知,我们把 3种类型的库进行了统一管理,使用 DatabaseModule 来集中管理
UserModule 为我们最终成品的测试模块,从这里可以看到封装的意义
database 模块设计
1 )database/database.module.ts 设计
import {
Module } from '@nestjs/common';
import {
TypeormCommonModule } from './typeorm/typeorm-common.module';
import {
PrismaCommonModule } from './prisma/prisma-common.module';
import {
MongooseCommonModule } from './mongoose/mongoose-common.module';
@Module({
imports: [
TypeormCommonModule,
PrismaCommonModule,
MongooseCommonModule
],
providers: [],
exports: [],
})
export class DatabaseModule {
}
- TypeormCommonModule
- 使用 TypeORM 作为 ORM 的公共模块
- 通常用于关系型数据库(如 PostgreSQL、MySQL)
- 封装了 TypeORM 的连接配置、实体注册、仓储服务等
- PrismaCommonModule
- 使用 Prisma ORM 的公共模块
- Prisma 是新一代的 Node.js ORM,支持 TypeScript,具有良好的类型推导
- 通常用于构建类型安全的数据访问层
- MongooseCommonModule
- 使用 Mongoose 的公共模块
- Mongoose 是 MongoDB 的 Node.js 驱动 ORM
- 适用于非关系型数据库(NoSQL)开发
2 )database/database.constants.ts 设计
export const TYPEORM_DB_CLIENT = 'TYPEORM_DB_CLIENT';
export const PRISMA_DB_CLIENT = 'PRISMA_DB_CLIENT';
export const IS_PRISMA = 'IS_PRISMA';
export const IS_TYPEORM = 'IS_TYPEORM';
export const IS_MONGOOSE = 'IS_MONGOOSE';
3 )database/database.interfaces.ts 设计
export interface ItenantRs {
isPrisma: Boolean,<
595



被折叠的 条评论
为什么被折叠?



