Loopback 4 中基于模式的多租户
Schema based Multi tenancy in Loopback 4
用例
在基于模式的多租户应用程序中,通常一个数据库具有多个模式。一个模式是存储公共应用程序数据的主要位置,一个模式用于应用程序的每个租户。每次新客户注册到系统时,都会在数据库中自动创建一个新的隔离模式。这意味着,模式是在 运行 时间创建的,并且事先不知道。客户的模式根据客户的域命名。当请求进入系统时,将验证用户并使用主模式上的数据 selected 模式。然后 most/all 后续数据库操作转到租户特定模式。如您所见,我们要使用的模式仅在 运行 时间已知。
问题
如何在 运行 时 select 架构?我们正在使用 postgres 连接器。我们应该能够在 运行 时间切换模式。
另一个问题是如何运行 为不同的租户迁移?
备注:
db-schema 需要以请求范围的方式设置,以避免为可能属于其他客户的其他请求设置模式。不能为整个连接设置架构。
您可以在运行时创建需要两个参数的 DefaultCrudRepository(使用遗留 juggler 模型和数据源的 CRUD 存储库的默认实现)
- 数据源
- 型号
现在使用所需设置实例化数据源,包括您要使用的模式
然后将您的模型和数据源提供给 DefaultCrudRepository 实例,如下所示:-
const ds = new PostgresDataSource({
connector: 'postgresql',
host: 'some host',
port: 'some port',
user: 'some user',
password: 'password',
database: 'database',
schema: 'schema for that particular tenant',
});
const repo = new DefaultCrudRepository(SomeModel, ds);
然后使用这个存储库执行查找、创建和其他方法。
有一个示例实现
https://github.com/hitesh2067/dynamic-schema-example
我已将架构作为查询参数传递,但您可以使用任何其他方式提供架构
更新的解决方案:-
现在Loopback已经为应用上下文和请求上下文提供了中间件和更好的上下文分离。我们可以使用它动态连接到任何数据源(希望连接器安装在 package.json 中)并将其绑定到应用程序上下文,然后将该绑定临时绑定到 UserRepository(或您的任何多租户)指向的数据源回购)在请求上下文中。
这个例子写在
https://github.com/dev-hitesh-gupta/loopback4-multi-tenant-multi-datasource-example
实现会像
const tenant = await this.getTenant(requestCtx);
if (tenant == null) return;
const tenantData = await this.tenantRepo.findById(tenant.id);
requestCtx.bind(CURRENT_TENANT).to(tenantData);
const tenantDbName = `datasources.multi-tenant-db.${tenantData.id}`;
// adding datasource if not present
if (!this.application.isBound(tenantDbName)) {
const tenantDb = new juggler.DataSource({
name: tenantDbName,
...tenantData.dbConfig,
});
this.application.bind(tenantDbName).to(tenantDb).tag('datasource');
}
// Pointing to a default datasource in request context
requestCtx
.bind('datasources.multi-tenant-db')
.toAlias(tenantDbName);
来自 LoopBack 团队的问候
LoopBack 4 不首先提供对多租户的 class 支持。我打开了一个 GitHub 问题来讨论不同的可能解决方案,请参阅 loopback-next#5056。
一个选项是调整特定于模型的存储库 class 以根据当前用户设置架构。这需要在 @loopback/repository
中实现一个小的增强功能,因此该解决方案目前无法开箱即用。我在下面交叉发布我评论的相关部分,请参阅 GitHub 问题了解完整上下文。
使用模式的轻量级租户隔离
在此设置中,身份验证层和所有租户共享相同的数据库名称并使用相同的凭据(数据库用户)来访问数据。我们在数据库中定义了 1+N 个模式:第一个模式由身份验证层使用,另外每个租户都有一个模式。所有数据库查询将使用相同的 LB 数据源,因此共享相同的连接池。
在实施方面,我们需要调整 LB4 模型向数据源注册的方式。我们不想为所有用户创建相同的后备 juggler 模型,而是要创建特定于租户的 juggler 模型。
从概念上讲,这可以通过调整存储库构造函数来实现。
export class ProductRepository extends DefaultCrudRepository<
Product,
typeof Product.prototype.id
> {
constructor(
@inject('datasources.db') dataSource: juggler.DataSource,
@inject(SecurityBindings.USER) currentUser: UserProfile,
) {
super(
// model constructor
Product,
// datasource to use
dataSource,
// new feature to be implemented in @loopback/repository:
// allow repository users to overwrite model settings
{schema: currentUser.name},
);
}
}
用例
在基于模式的多租户应用程序中,通常一个数据库具有多个模式。一个模式是存储公共应用程序数据的主要位置,一个模式用于应用程序的每个租户。每次新客户注册到系统时,都会在数据库中自动创建一个新的隔离模式。这意味着,模式是在 运行 时间创建的,并且事先不知道。客户的模式根据客户的域命名。当请求进入系统时,将验证用户并使用主模式上的数据 selected 模式。然后 most/all 后续数据库操作转到租户特定模式。如您所见,我们要使用的模式仅在 运行 时间已知。
问题
如何在 运行 时 select 架构?我们正在使用 postgres 连接器。我们应该能够在 运行 时间切换模式。
另一个问题是如何运行 为不同的租户迁移?
备注:
db-schema 需要以请求范围的方式设置,以避免为可能属于其他客户的其他请求设置模式。不能为整个连接设置架构。
您可以在运行时创建需要两个参数的 DefaultCrudRepository(使用遗留 juggler 模型和数据源的 CRUD 存储库的默认实现)
- 数据源
- 型号
现在使用所需设置实例化数据源,包括您要使用的模式 然后将您的模型和数据源提供给 DefaultCrudRepository 实例,如下所示:-
const ds = new PostgresDataSource({
connector: 'postgresql',
host: 'some host',
port: 'some port',
user: 'some user',
password: 'password',
database: 'database',
schema: 'schema for that particular tenant',
});
const repo = new DefaultCrudRepository(SomeModel, ds);
然后使用这个存储库执行查找、创建和其他方法。 有一个示例实现 https://github.com/hitesh2067/dynamic-schema-example
我已将架构作为查询参数传递,但您可以使用任何其他方式提供架构
更新的解决方案:-
现在Loopback已经为应用上下文和请求上下文提供了中间件和更好的上下文分离。我们可以使用它动态连接到任何数据源(希望连接器安装在 package.json 中)并将其绑定到应用程序上下文,然后将该绑定临时绑定到 UserRepository(或您的任何多租户)指向的数据源回购)在请求上下文中。
这个例子写在 https://github.com/dev-hitesh-gupta/loopback4-multi-tenant-multi-datasource-example
实现会像
const tenant = await this.getTenant(requestCtx);
if (tenant == null) return;
const tenantData = await this.tenantRepo.findById(tenant.id);
requestCtx.bind(CURRENT_TENANT).to(tenantData);
const tenantDbName = `datasources.multi-tenant-db.${tenantData.id}`;
// adding datasource if not present
if (!this.application.isBound(tenantDbName)) {
const tenantDb = new juggler.DataSource({
name: tenantDbName,
...tenantData.dbConfig,
});
this.application.bind(tenantDbName).to(tenantDb).tag('datasource');
}
// Pointing to a default datasource in request context
requestCtx
.bind('datasources.multi-tenant-db')
.toAlias(tenantDbName);
来自 LoopBack 团队的问候
LoopBack 4 不首先提供对多租户的 class 支持。我打开了一个 GitHub 问题来讨论不同的可能解决方案,请参阅 loopback-next#5056。
一个选项是调整特定于模型的存储库 class 以根据当前用户设置架构。这需要在 @loopback/repository
中实现一个小的增强功能,因此该解决方案目前无法开箱即用。我在下面交叉发布我评论的相关部分,请参阅 GitHub 问题了解完整上下文。
使用模式的轻量级租户隔离
在此设置中,身份验证层和所有租户共享相同的数据库名称并使用相同的凭据(数据库用户)来访问数据。我们在数据库中定义了 1+N 个模式:第一个模式由身份验证层使用,另外每个租户都有一个模式。所有数据库查询将使用相同的 LB 数据源,因此共享相同的连接池。
在实施方面,我们需要调整 LB4 模型向数据源注册的方式。我们不想为所有用户创建相同的后备 juggler 模型,而是要创建特定于租户的 juggler 模型。
从概念上讲,这可以通过调整存储库构造函数来实现。
export class ProductRepository extends DefaultCrudRepository<
Product,
typeof Product.prototype.id
> {
constructor(
@inject('datasources.db') dataSource: juggler.DataSource,
@inject(SecurityBindings.USER) currentUser: UserProfile,
) {
super(
// model constructor
Product,
// datasource to use
dataSource,
// new feature to be implemented in @loopback/repository:
// allow repository users to overwrite model settings
{schema: currentUser.name},
);
}
}