是否有丰富的域模型示例?

我正在寻找一个简单的例子来说明使用丰富域模型的好处。 理想情况下,我想要一个前后代码列表(应该尽可能短)。

前面的代码清单应该显示使用贫血域模型解决的问题,以及许多相当程序化的服务层代码,后面的代码清单应该显示使用丰富的面向对象的域模型解决的相同问题。

理想情况下,代码清单应该是Java或Groovy,但任何相似的东西(例如C#)都可以。

我将为您提供一个真实生产代码的简单示例:

Person.groovy:

List addToGroup(Group group) { Membership.link(this, group) return groups() } 

Membership.groovy:

  static Membership link(person, group) { def m = Membership.findByPersonAndGroup(person, group) if (!m) { m = new Membership() person?.addToMemberships(m) group?.addToMemberships(m) m.save() } return m } 

每当我想将一个人绑定到一个组时,我就可以做person.addToGroup(group)

程序代码就像这样,在你的控制器上:

 def m = Membership.findByPersonAndGroup(person, group) if (!m) { m = new Membership() person?.addToMemberships(m) group?.addToMemberships(m) m.save() } 

乍一看,你可以说你可以把它包装在一个函数中,你很高兴。 但富域设计恕我直言的优势在于它与您的思维方式更接近,因此更接近合理化。 在这个特定的例子中,我只想将一个人添加到一个组中,代码将只读取它。

这是一个简短的例子,就像你问的那样,但是很容易扩展这个例子,并且看到你可以通过适当的域建模来构建复杂的交互。

您还可以看到Martin Fowler的交易脚本与域模型 ,以简要解释这两种模式,我认为这些模式与DDD有关。

也许Jimmy Bogard的这个演讲包含了你的要求

https://github.com/jbogard/presentations/tree/master/WickedDomainModels

我认为没有人做过那种比较,如果有的话,那就不会那么小了。 域驱动设计试图解决复杂性,一个简单的例子不包含复杂性。

也许Domain Driven设计Step by Step会给你一些答案。

这并不能完全回答您的问题,但我认为域驱动设计与数据库驱动设计相反。 在数据库驱动的设计中,首先创建数据库模式,然后在完全了解模式外观的情况下创建类。 优点是您可以更好地了解“引擎盖下”发生的情况,并最大限度地减少阻抗不匹配的影响。 然而,缺点是数据库模式,因为它是关系而不是面向对象,不会很好地转换为对象(例如,关系数据库中没有集合的概念)。

在域驱动设计中,理论上您可以像创建任何其他类一样创建数据对象,并将数据库视为一个持久层。 在Layman的术语中,数据库只是一个存储容器,你不关心对象是如何存储的,只是它们以某种方式存储。 这消除了阻抗不匹配,您可以少担心一件事。 但是,在实践中,您仍然需要了解对象的存储方式,并且当您使用的ORM尝试吐出复杂的SQL查询时,可能会出现性能问题。

编辑:

这是一个原则上应该是域驱动设计的例子。 假设你有一个Person类,就像这样(在C#中):

 public class Person { public int Id { get; set; } public string Name { get; set; } public Address Address { get; set; } public ICollection Relatives { get; set; } public Company Employer { get; set; } } 

现在,在关系数据库中,这可能会转换为3个表,一个Person表,Address表和Company表,它们之间有很多关系。 但是,这与程序员看到此对象的方式大不相同。 程序员将其视为具有4个参数的Person对象的实例,其中一个参数是ICollection 。 这与数据库表结构不匹配,因此“阻抗不匹配”,或者在Laymen的术语中,关系模型和对象模型之间的布局差异。

在域驱动设计中,我应该能够这样做:

 Person person = new Person(); // set each property to something Database.Save(person); 

现在,人物对象被保存。 我可以像这样检索它:

 Person databasePerson = Database.Get(idOfPerson); 

并且它将返回我的Person对象,就像我保存它之前的那样。 这样,我完全不关心数据库如何保存它,或者担心阻抗不匹配。 我只需保存它并根据需要检索它。

但这完全是理论上的。 实际上,您可能必须手动指定“映射”,或者类如何知道数据库中的哪个表/列从中获取数据。 当您尝试映射到更复杂的类型(如字典和其他ADT)时,以及当您尝试将数据从多个表拉入一个类时,它会变得非常复杂。