【问题标题】:Designing multi-tenant app in Rails在 Rails 中设计多租户应用程序
【发布时间】:2017-10-21 04:02:58
【问题描述】:

我正在使用 Rails 实现一个多租户应用程序。我的方法是不使用 postgres 内置的多租户功能并添加一列来记录子域。这就是问题所在:)

让我们来看看这个例子

class Organisation < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :organisation
end

我在这里考虑两种方法:

方法 1

仅将subdomain 列添加到organisations

  • 优点 - 关系数据库的工作方式\0/
  • 缺点 - 当我有更复杂的查询时,这会使我的代码变慢

方法 2

subdomain 列添加到organisationsusers

  • 优点 - 这将使查询更快
  • 缺点 - 我要反对关系数据库

所以问题是,在以上两种方法之间我应该遵循什么样的方法,或者有没有我没有想到的不同方法?

【问题讨论】:

  • 我看到有人投票结束这个问题,因为这是一个基于意见的问题。但是我不同意它,因为这个问题更多的是关于设计决策,我已经概述了我的想法。我不是在问什么是最好的编辑器 VIM 或 Sublime ;)。

标签: ruby-on-rails ruby-on-rails-5 multi-tenant


【解决方案1】:

我们运行一个多租户 Rails 应用程序,其支持表的类略少于 500 个,如果我不得不猜测,我会说其中大约 400 个与客户端数据有关。

客户端特定的属性保存在Client 模型中,但我们将client_id 添加到每个客户端表中,并且对数据库具有非空约束。但只有少数被索引,因为它们通常只能通过父记录访问。

我们不必担心设置客户端 id,因为模型通常会有:

class Child

  after_initialize do
    self.client ||= parent.client
  end

end

我们将client_id 添加到许多表中,因为我们有很多代码:

@books = current_user.client.books

...所以在这些情况下,我们将直接从Client 关联到模型,并为client_id 编制索引。

我们将client_id 添加到所有表中,但是,因为我们经常出于操作或不寻常的原因,希望能够找到客户的所有相关记录...

MarketingText.where(client: Client.snowbooks).group(:type).count

...而且必须通过父记录很不方便。

此外,由于我们决定对所有客户特定的表执行此操作,因此我们不必对每个表都做出决定。

所以要回答您的问题,我要做的是将子域仅添加到 Organisation。但是,我会将organisation_id 列添加到每个包含组织特定数据的表中。

如果您有很多客户,但您通常会熟悉他们的子域,那么我会在组织上编写一个元程序方法,让您使用:

Organisation.some_subdomain

...获取所需的组织,然后直接从组织模型中找到具有关联的子记录(在任何表中)...

Organisation.some_subdomain.users
Organisation.some_subdomain.prices
Organisation.some_subdomain.whatevers

【讨论】:

  • 你如何处理关联?如果您有 belongs_to :something ,您如何防止用户发布属于其他组织的 id?
  • @GorillaApe 我认为这是过滤用户可以看到的内容的组合,例如通过引用current_user.client.invoices 而不是Invoice.all,并且可以。
【解决方案2】:

我的意见是方法一,这有几个原因

  1. 使用由 activerecord + 范围提供的关系数据库将使编写软件变得更容易,如果您稍后在组织中有更多对象,例如事务、项目(除了用户),

我有一个具有多租户功能的项目,下面是我项目中的设计示例

class Company < ApplicationRecord
    has_many :users     
  # transaction
    has_many :transactions
    has_many :journals , :through => :transactions
  # item
    has_many :items
    # other has_many ...
end

在控制器中,您可以使用预先加载来最小化查询(包括/连接)

@company.includes(:users).scope_filter_here.search(params[:q])
  1. 与方法2相比,方法1对用户更友好,因为用户编写您的url地址更简单,输入的url越少越好(个人意见)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-29
    • 2017-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多