Ruby on Rails – STI的替代品?

我有许多不同的模型(接近20个),它们有一些共同的属性,但在某些程度上也有所不同。 STI起初看起来很有吸引力,但我不知道随着产品的快速发展,各种模型会如何随着时间的推移而发展。

与我们的应用程序很好地平行的是Yelp。 Yelp如何在Rails中管理一些东西? 所有post都有一些共同的属性,如“地址”。 然而,他们在其他方面存在很大差异。 例如,您有餐厅的预订选项,而其他人可能没有。 餐馆还有许多其他属性,如“允许饮酒”,不适用于他人。 使用STI执行此操作将很快失控。

那么下一个最佳选择是什么? 与Postgres的HStore? 我不习惯使用HStore来做任何事情。 HStore解决了一些问题,同时介绍了其他问题,如缺乏数据类型,缺乏参照完整性检查等。我希望有一个可靠的关系数据库作为构建的基础。 所以在Yelp案例中,可能是餐馆模特是我要去的地方。 我已经看过像这里的建议 – http://mediumexposure.com/multiple-table-inheritance-active-record/ ,但是我很乐意做这么多的猴子修补以获得如此常见的东西。

所以我想知道还有其他替代方案(如果有的话)或者我应该咬紧牙关,磨牙并将这些常见属性复制到20个模型中? 我认为我的问题将来自迁移文件而不是代码本身。 例如,如果我将迁移设置为循环遍历表并在表上设置这些属性,那么我是否可以通过使用不同的模型来减轻问题的严重程度?

我是否会忽略一些可能导致大量问题的重要问题,而这些问题将会产生一些单独的模型?

我在这里看到几个选项:

  1. 咬紧牙关,创建具有许多相同属性的20种不同模型。 这些模型可能会随着时间的推移而漂移 – 为一种特定类型添加新字段 – 您将创建一个带有STI的200列表。 也许你没有 – 未来很难看到,尤其是探索/敏捷软件。

  2. 将非引用字段存储在NoSQL(文档)数据库中。 将关系数据库用于关系的记录部分(用户有很多评论,评论有一个业务),但是将类型特定的东西保存在NoSQL数据库中。 在您的Noails文档架构中保留Rails模型和external_record_id / external_record_type中的external_record_type ,这样您仍然可以使用您最终使用的NoSQL ORM查询允许吸烟的所有栏。

  3. 创建属性模型。 属性belongs_to :parent_object, polymorphic: true具有keyvalue字段的belongs_to :parent_object, polymorphic: true 。 使用此方法,您可能拥有基本Business模型,并且每个业务都可以具有has_many :attributes 。 业务的某些(非关系?)属性( allows_smoking )是一个Attribute记录。 Attribute的键可以是一个字符串,也可以是一个Ruby常量的数字。 您实际上是使用Attribute实体来创建选项#2的SQL版本。 这可能是一个不错的选择,我自己也将它用于User或Profile模型。 (虽然这种方法有一些性能点击要注意)。

我真的担心有那些听起来像子类的东西的那么多(独立)模型。 有可能你可以通过使用Concerns来解决常见的行为/方法(关于mixin概念的语法糖,请参阅关于Rails 4中关注点的一个很棒的SO答案 )。 当然,你仍然有(初始)迁移问题。

在此添加另一个选项: 序列化LOB (272)。 ActiveRecord允许您使用serialize对对象执行此操作:

class User

user = User.create(preferences:{“background”=>“black”,“display”=> large})User.find(user.id).preferences#=> {“background”=>“black”,“显示“=>大}

(来自ActiveRecord :: Base docs的示例代码。)

要理解的重要结果是存储在序列化LOB中的属性将不可索引,并且当然不能以任何高效的方式进行搜索。 如果你后来发现一个列需要作为索引可用,你必须编写[最有可能]一个Ruby程序来执行转换(尽管默认序列化在Yaml中,所以任何Yaml解析器就足够了)。

优点是您无需对堆栈进行任何技术更改即可应用此模式。 根据您收集的数据量,它很容易适度 – 从这种模式迁移出来。