新手问题 SaaS 服务里,显示的按 “公司” 信息进行显示的,有没有什么优雅的解决方案?

QueXuQ · 2014年04月13日 · 最后由 pynix 回复于 2014年04月15日 · 2360 次阅读

我们在做一种服务,以公司分类,没人进来注册,提供公司资料。

然后登录进去之后,可以创建用户,创建用户也可以登录进来,管理库存,订单等事情。

因为每个用户进来,只可以管理自己公司的事情。所以我对所有的产品,订单和用户都加上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 会累死的。

关键点:

  1. Model 里面加default_scope。映射到 current_tenent.

  2. ApplicationController 里面保证有 tenant,并且要写到一个除 controller 之外的地方,不然 model 用不了。

  3. 一定一定要保证 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

@hz_qiuyuanxin

第一,你的 API Project.current 和楼主的current_company.projects没有本质的区别,都是要手动加 current 这个 scope。并没有提供任何便利。

第二,凡是带两个@@的都可能是有气味的。尤其你的 class variable 竟然直接设到 ActiveRecord::Base 更是设计的大忌。我不确定这种设置要是同时几个用户访问,他们会不会得到同样的 comapny_id :)

如果你们够雄心的话直接使用容器来隔离每个应用的数据了,类似 PAAS,公司的数据可能比较隐私,如果显示错乱了就麻烦了,另外,设计上也应该没那么复杂了。

#3 楼 @billy 谢谢提醒,确实存在这样的问题。

#4 楼 @pynix 能否再详细点,或者给个资料?我也想研究一下这种问题

#4 楼 @pynix 好像挺有意思,但是完全不懂。可以提供点资料不?

#1 楼 @billy 非常感谢,看了那个 railscasts 后,清晰很多。但是一直有一个疑问:

def self.current_id=(id)
  Thread.current[:tenant_id] = id
end

def self.current_id
  Thread.current[:tenant_id]
end

这样就一定可以保证线程的安全了?

#9 楼 @QueXuQ Thread.current 这种做法,本质也是用全局变量的方式。

@QueXuQ 我没有实际做过这种 multi tenancy 的项目,但从代码上看是可以保证线程安全的。因为写值是写入现有线程,其他线程是拿不到同样的值的。

@hz_qiuyuanxin 准确说应该是现有线程的全局变量。其他全局变量,以及cattr_accessors, @@foo 这些都是线程不安全的。

#6 楼 @hz_qiuyuanxin #8 楼 @QueXuQ

每个公司开启一个实例,应用进程,数据库等完全隔离,可以试试目前比较火的 docker,总之数据隔离,还能简化设计,你那个 company_id 就不用放到每个表里了。

@pynix docker 好像很有意思,谢谢,涨姿势了。

#12 楼 @pynix 谢谢,这个真不错,应该会有个主机能新建,管理、监控这些 dockers 的。

pynix PAAS 那些事 提及了此话题。 04月03日 10:57
需要 登录 后方可回复, 如果你还没有账号请 注册新账号