Loopback 4 中基于模式的多租户

Schema based Multi tenancy in Loopback 4

用例

在基于模式的多租户应用程序中,通常一个数据库具有多个模式。一个模式是存储公共应用程序数据的主要位置,一个模式用于应用程序的每个租户。每次新客户注册到系统时,都会在数据库中自动创建一个新的隔离模式。这意味着,模式是在 运行 时间创建的,并且事先不知道。客户的模式根据客户的域命名。当请求进入系统时,将验证用户并使用主模式上的数据 selected 模式。然后 most/all 后续数据库操作转到租户特定模式。如您所见,我们要使用的模式仅在 运行 时间已知。

问题

如何在 运行 时 select 架构?我们正在使用 postgres 连接器。我们应该能够在 运行 时间切换模式。

另一个问题是如何运行 为不同的租户迁移?

备注:

db-schema 需要以请求范围的方式设置,以避免为可能属于其他客户的其他请求设置模式。不能为整个连接设置架构。

您可以在运行时创建需要两个参数的 DefaultCrudRepository(使用遗留 juggler 模型和数据源的 CRUD 存储库的默认实现)

  1. 数据源
  2. 型号

现在使用所需设置实例化数据源,包括您要使用的模式 然后将您的模型和数据源提供给 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},
    );
  }
}