【问题标题】:What are the benefits of Persistence Ignorance?执着无知有什么好处?
【发布时间】:2010-10-28 16:14:08
【问题描述】:

我是 DDD+TDD 世界的新手。但我从事编程工作将近 9 年了。

有人可以解释一下坚持无知的好处吗?典型的 nHibernate 应用程序只是将类和数据库之间的依赖关系推送到映射文件。

如果我更改类文件或数据库,我必须更改映射文件。那么它不只是通过添加一个抽象层来推动依赖吗?在我看来,到目前为止,我不认为这是革命性的。但我不确定我是否遗漏了什么。

最后我如何测试映射文件?映射文件中可能会出现错误,我该如何测试它们?

【问题讨论】:

  • 我在答案中添加了一个关于测试的部分。
  • 听了Architecture: The Lost Years之后就爱上了Repository模式。每个严肃的应用程序都应该使用存储库模式。

标签: domain-driven-design persistence-ignorance


【解决方案1】:

PI 不是关于使用 NHibernate。 PI 代表忽略数据将如何存储开发领域模型。是的,它通过添加一个抽象层来推动依赖。 DDD 不是革命性的——它更像是一种想法,一种如何使用已经熟悉的模式(大多数都是)进行编码的方法。 IE。 - 工厂模式或模块模式也不是新的,但在 DDD 中相当重要的一部分。

我最近也开始使用 NHibernate,所以 - 无法提供有关它的详细信息。但我得到了一个可能对您有用的提示 - 如果您还没有这样做,请尝试 Fluent NHibernate

【讨论】:

  • 谢谢 Arnis,我不是说 PI=nHibernate。我只是用它作为一个例子。 Fluent Nhibernate 不错,我去看看。但问题是,如果我致力于将 SQL Server 作为数据库,那么使用 nHibernate 有什么真正的好处吗?
  • 我喜欢 NHibernate 提供了相当干净的方式来提供持久性(至少 - 到目前为止)。但最重要的是,我喜欢它将数据(几乎)直接映射到我的 POCO(en.wikipedia.org/wiki/POCO)。我相信它最适合 DDD。
【解决方案2】:

我一直在考虑域,虽然我过去使用过存储过程、ADO.NET,但直到我最终迁移到 NHibernate 时,我才对我的持久性机制感到满意。

领域驱动设计 (DDD) 将重点直接放在领域模型上。这意味着主要关注点是创建一个概念模型,该模型为用户和程序员形成一种通用语言。用户几乎从不对您如何保存他们的信息感兴趣。 NHibernate 通过使持久性成为捕获业务规则和了解用户真正想要从系统中得到什么的次要关注点来帮助您实现这种心态。

Fluent NHibernate 减少了对域模型的更改对底层映射文件的影响。它也有auto mapping。虽然您永远不能完全忽略系统的持久性,但 NHibernate 与 Fluent NHibernate 允许您专注于域模型。如果您不专注于使用丰富的域模型,那么 NHibernate 几乎没有什么好处。

关于测试您的映射,无论您使用什么方法来实现持久性,您都将编写测试(或者您应该编写测试)。这不是因为您使用 NHibernate 而出现的额外工作。只需将测试您的映射视为测试您的持久性是否正常工作。

再次证明Fluent NHibernate 是无价的。它有一个Persistence Specification Testing,在大多数情况下使用起来非常简单。

【讨论】:

  • 该死的……你肯定打败了我。 :D
  • 如何在使用 DI 时测试映射文件?模拟永远不能保证代码和数据库之间的实际映射?我唯一的选择是在没有映射的情况下在测试中使用实际的持久性?
  • FluentNHibernate 不是 NHibernate 的官方部分。他们甚至拒绝将其作为贡献。 NHibernate 可能会在下一个主要版本(3.0)中拥有自己的编程映射配置。
  • +1 用于提及 FNH 自动映射。 IME,这是获取域模型映射的最快、最简单的方法。
  • @Stefan - FNH 可能不是 NH 的一部分,但它减少了很多痛点。然而,他们不知道他们拒绝做出贡献。这是非常不幸的 - 如果它们更紧密地集成,对每个人都会更好。
【解决方案3】:

让我用一个例子来解释一下。假设您正在使用经典的 SQL 方法实现应用程序。您打开记录集、更改数据并提交它。

伪代码:

trx = connection.CreateTransaction();
query = connection.CreateQuery("Select * from Employee where id = empid");
resultset = query.Run();
resultset.SetValue("Address_Street", "Bahnhofstrasse");
resultset.SetValue("Address_City", "Zürich");
trx.Commit();

使用 NHibernate,它看起来像这样:

emp = session.Get<Employee>(empid);

// persistence ignorant 'logic'
emp.Address.Street = "Bahnhofstrasse";
emp.Address.City = "Zürich";

session.Commit();

Persistence Ignorance 意味着业务逻辑本身不知道持久性。 或者换句话说,持久性与逻辑是分开的。这使得它更加可重用。

将“逻辑”移至可重用方法:

void MoveToZuerichBahnhofstrasse(Employee emp)
{
  // doesn't have anything to do with persistence
  emp.Address.Street = "Bahnhofstrasse";
  emp.Address.City = "Zürich";
}

尝试使用结果集编写这样的方法,您就会知道什么是持久性无知。

如果你不相信,看看单元测试会有多简单,因为与持久性相关的东西没有任何依赖关系:

Employee emp = new Employee();
MovingService.MoveToZuerichBahnhofstreasse(emp);
Assert.AreEqual("Bahnhofstrasse", emp.Address.Street);
Assert.AreEqual("Zürich", emp.Address.City);

DDD 是不同的东西。在那里你首先建立你的领域模型(类模型)并根据它创建数据库设计。使用 NH,这非常简单,因为 - 由于对持久性的无知 - 您可以在拥有(确定的)数据库模型之前编写和单元测试模型和逻辑。


测试:我们通过创建实体实例、将其存储到数据库、取回并进行比较来测试映射。这是通过大量反射自动完成的。但你不需要走那么远。尝试存储实体时会出现大多数典型错误。

您可以对查询执行相同的操作。复杂的查询值得测试。如果查询完全被编译,那将是最有趣的。您甚至不需要任何数据。

对于数据库集成测试,我们使用Sqlite。这是相当快的。 NH 使用 SchemaExport 动态生成内存数据库(在每次测试之前)。

【讨论】:

  • 感谢 Stefan 的精彩回复。
  • 是否有一些关于持久性无知的书或资源可以学习?
猜你喜欢
  • 2010-12-30
  • 1970-01-01
  • 2019-06-15
  • 2011-11-11
  • 1970-01-01
  • 2011-01-04
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
相关资源
最近更新 更多