在 Ruby/Rails 中隔离数据访问层的最佳实践

Posted

技术标签:

【中文标题】在 Ruby/Rails 中隔离数据访问层的最佳实践【英文标题】:Best practice to isolate data access layer in Ruby/Rails 【发布时间】:2011-06-29 22:25:39 【问题描述】:

所以,我有 Ruby on Rails 应用程序。暂时空白。让我从一开始就说,我的大部分经验都来自 Java,所以我可能不会像 RoR 开发人员那样思考。 :-)

我需要做的是创建一些数据访问层,说它将是访问用户,所以让它成为 UserDAO.rb 基本上会使用 ActiveRecord 直接访问数据库访问一些键值存储 em> 其他我能想到的。

从技术上讲,由于我们在 Ruby 中没有接口,我可以让 UserDAO.rb “拥有”实现(基本上,我说的是组合),这可能是我们需要的任何东西,比如 UserDAOActiveRecord.rb 或 UserDAOMongo .rb 或其他类似的东西。 UserDAO.rb 基本上会调用实现的方法,仅此而已。应该很容易在实现之间切换。

虽然这听起来像是一个可能的解决方案,但我期待听到在 Ruby 世界中解决这个问题的最佳实践是什么。谢谢!

【问题讨论】:

ActiveRecord 为您抽象了数据库层。从 PostgreSQL 到 mysql 到 MongoDB 的切换发生在您的应用程序之下。 【参考方案1】:

您将不得不寻找 ActiveRecord 以外的 Ruby 类(正如所指出的,它是一个对象关系映射器,因此没有单独的数据访问层)。

你可能想看看:https://github.com/jeremyevans/sequel

您可以创建一个类Person,其中包含使用 Sequel 实例与数据库通信的方法。

这段未经测试的代码说明了为什么这可能不是一个好主意:

class Person
  attr_reader :first_name, :last_name

  DataSource = Sequel.sqlite('my_app.db')[:people]

  def initialize(record)
    @first_name = record.first_name
    @last_name = record.last_name
  end

  # Find a record from the database and use it to initialize a new Person object 
  def self.find_by_id(id)
    self.new(Table.where(:id => id))
  end
end

【讨论】:

要补充一点,似乎没有“转到”插件来拥有单独的数据和业务逻辑层。我确实认为这是 Ruby/Rails 中缺少的东西。顺便说一句,在 Rails 3 中,为模型使用 ActiveRecord 以外的东西要容易得多。 现在有 ROM(Ruby 对象映射器)rom-rb.org。对于关系数据库,它在后台使用 Sequel gem。【参考方案2】:

请记住,ActiveRecord 不仅是一种访问数据库的方式,它还是一种将数据集成到应用程序中的模式:每个模型控制自己的数据并根据需要存储/检索/查询数据,带有表示数据库行的模型实例。

当然,您不必使用该模式,但它是 Rails 的核心之一,因此将 ActiveRecord 视为另一种要抽象的数据访问方法,您会失去很多功能。

另请注意,ActiveRecord 已经通过使用数据库适配器抽象出数据库类型,因此很容易放入 MySQL、Oracle 等。但它确实假设了一个关系数据库。

但是,要回答您的问题,实际上没有必要将您的数据访问实现包装在另一个类中以确保接口一致。正如您所说,ruby 没有 Java 类型的接口,但 ruby​​ 世界通常也不会尝试确保开发人员只能做合法的事情。您可以创建一组提供相同方法集的数据访问类,创建单元测试以确保这些方法是一致的(并充当这些方法的可执行文档),然后信任开发人员对任何一个进行正确调用他们选择的实施。这与 Java 世界有很大的文化差异,Java 世界中的一切都被编码为接口,方法是最终的,对象是不可变的。这需要一些时间来适应。

【讨论】:

好的,我知道关于数据库/关系访问的抽象。但是,如果我需要更改它,它将使用一些键值存储,并在它成为“对象”之前以特定的方式解密值 Ruby 的优点之一是一切都是可修改的。活动记录的工作方式是,如果我要求 person.first_name,它会将“first_name”视为对不存在的方法的调用,并且在 Person 类method_missing 类中,它会调用数据库.因此,您可以覆盖 method_missing 并从那里进行自己的检索调用。同样,此时您可以从缓存中检索。 @JacobMattison 我同意你的观点。尽管如此,我认为在非常复杂的应用程序中,Activerecords(或任何 ORM)可以而且应该放在它自己的数据访问层中,并且只在数据访问层中使用,以简化访问并通过 DTO(或直接)将结果映射到业务层与其他层或外部形式等没有依赖关系(如果您严格遵循 DDD)。通过这种更清晰的分离,您可以在不破坏业务逻辑的情况下尽可能切换 orm。也不是每一层都需要有一个数据库连接......【参考方案3】:

Alex,我强烈建议您阅读 Agile Web Devoplment With Rails(第 4 版)一书。 Active Record实现了...活动记录模式,所以Class就是DAO(类方法代表dao),而实例方法代表对象。

所以例如你可以有

person = Person.find(3)   //Person is the dao
person.name = 'mick'      //name = is the setter for he instance
person.save               // well.. saves the object

我已经用 Java 编写代码 10 年了,刚开始使用 ruby​​ ......这是一个相当大的变化。

【讨论】:

好的,所以说我需要改变它的工作方式——我希望“Person”有我的实现——并且可能不是 SQL 的,而是非常不同的东西。我该如何改变它?另外,如果我决定缓存一些片段,最好的方法是什么?我想尽可能地让控制器远离这个头痛。

以上是关于在 Ruby/Rails 中隔离数据访问层的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

ruby/rails:如何确定是不是包含模块?

如何使用ruby / rails将所有大写字母转换为适当的首字母大写字母,其余为小写?

ruby rails中如何配置puma服务监听指定的IP地址

Ruby/Rails - 为啥自我关联条件取决于创建时间?

在 winforms 和 web 应用程序之间共享数据层和模型层的最佳方法

业务逻辑和数据访问层的循环依赖