我们在做一种服务,以公司分类,没人进来注册,提供公司资料。
然后登录进去之后,可以创建用户,创建用户也可以登录进来,管理库存,订单等事情。
因为每个用户进来,只可以管理自己公司的事情。所以我对所有的产品,订单和用户
都加上company_id
。(这个做法应该是正确的吧?)
但是其中一个不优雅的地方,在 application controller 里加入:
def current_company
@current_company = current_user.company
end
会出现大量的current_company
,如:
@projects = @current_company.projects
@orders = @current_company.orders
@users = @current_company.users
觉得是不是有更好的解决方案呢?
你这是标准的 multi tinency,的确有更好的方案。要是都这么手动加 scope 会累死的。
关键点:
Model 里面加default_scope
。映射到 current_tenent.
ApplicationController 里面保证有 tenant,并且要写到一个除 controller 之外的地方,不然 model 用不了。
一定一定要保证 Thread safty。
可以看看这些参考: https://github.com/influitive/apartment https://github.com/ErwinM/acts_as_tenant http://railscasts.com/episodes/388-multitenancy-with-scopes?view=asciicast (纯手工,无 gem)
def current_company
# @current_company = current_user.company
# 换成下面会更好
@current_company ||= current_user.company
end
其他地方直接调用 current_company
而不是 @current_company
建议不要直接改写 model 的 default_scope,直接写个 concerns,然后让 ActiveRecord::Base 去 include 它就好了
module CurrentCompany
extend ActiveSupport::Concern
module ClassMethods
def on_company(company)
where(company_id: company.id)
end
end
end
ActiveRecord::Base.send :include, CurrentCompany
第一,你的 API Project.current
和楼主的current_company.projects
没有本质的区别,都是要手动加 current 这个 scope。并没有提供任何便利。
第二,凡是带两个@@的都可能是有气味的。尤其你的 class variable 竟然直接设到 ActiveRecord::Base 更是设计的大忌。我不确定这种设置要是同时几个用户访问,他们会不会得到同样的 comapny_id :)
@QueXuQ 我没有实际做过这种 multi tenancy 的项目,但从代码上看是可以保证线程安全的。因为写值是写入现有线程,其他线程是拿不到同样的值的。
@hz_qiuyuanxin 准确说应该是现有线程的全局变量。其他全局变量,以及cattr_accessors
, @@foo
这些都是线程不安全的。
#6 楼 @hz_qiuyuanxin #8 楼 @QueXuQ
每个公司开启一个实例,应用进程,数据库等完全隔离,可以试试目前比较火的 docker,总之数据隔离,还能简化设计,你那个 company_id 就不用放到每个表里了。