简体   繁体   中英

Designing multi-tenant app in Rails

I'm implementing a multi-tenant app with Rails. My approach is not to use the postgres inbuilt multi-tenant feature and add a column to record the subdomain. That is where the question is :)

Let's get this example

class Organisation < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :organisation
end

I'm thinking about two approaches here:

Approach 1

add a subdomain column only to organisations

  • pros - The way how relational databases should work \\0/
  • cons - When I have more complex queries , that will make my code slow

Approach 2

add a subdomain column to both organisations and users

  • pros - This will make queries faster
  • cons - I'm going against the relational databases

So the question is, what sort of a method I should follow between above two, or are there a different approach that I didn't think about?

my opinion will go Approach number one, couple reasons for this

  1. using relational database provided with activerecord + scopes will make writing software easier, also if you have more objects under organization later for example transactions, items (beside users),

I have a project with multi tenant capabilities and below is sample of design in my project

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

and in controller you can use eager loading to minimize query (includes / joins)

@company.includes(:users).scope_filter_here.search(params[:q])
  1. approach number 1 is more user friendly compared with approach number 2 as it's more simple to user writing your url address, the less url to type is better (personal opinion).

We run a multi-tenant Rails app with slightly fewer than 500 table-backed classes, and if I had to guess I'd say around 400 of them relate to client data.

Client-specific attributes are held in the Client model, but we add the client_id to every client table with a not null constraint on the database. Only a minority of them are indexed, though because they are generally only accessed through a parent record.

We do not have to worry about setting the client id because the model will generally have:

class Child

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

end

We add the client_id to many tables because we have a lot of code that does:

@books = current_user.client.books

... so in those cases we'll have an association directly from Client to the model, and client_id is indexed.

We add the client_id to all tables, though, because we very often, for operational or unusual reasons, want to be able to find all of the relevant records for a client ...

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

... and having to go through a parent record is just inconvenient.

Also, because we made the decision to do this on all client-specific tables, we do not have to make the decision on each one.

So to get to your question, what I would do is add the subdomain to the Organisation only. However, I would add the organisation_id column to every table holding Organisation-specific data.

If you have a lot of clients but you are going to be generally familiar with their subdomain, then I would write a meta-program method on the Organisation that lets you use:

Organisation.some_subdomain

... to get the required organisation, then find the child records (in any table) with an association directly from the Organisation model ...

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM