不可变对象和DDD是否在一起?


18

考虑使用DDD的系统(同样:使用ORM的任何系统)。实际上,在几乎每个用例中,任何系统的重点都是操纵这些域对象。否则,就没有真正的效果或目的。

修改一个不可变的对象会导致该对象持久化后生成新记录,这会在数据源中造成巨大的膨胀(除非您在修改后删除了以前的记录)。

我可以看到使用不可变对象的好处,但是从这个意义上讲,我从来没有看到使用不可变对象的有用案例。这是错的吗?


Answers:


16

使用不可变的对象进行计算(如在函数编程中一样)不一定意味着要持久化所生成的每个对象!


我没有看到这种情况下无论如何都使用不可变对象的实例-因此它们没有特定目的。我错了吗?
史蒂文·埃弗斯

1
我以为不可变对象对于中间计算(在并行线程中)将很有用
Steven A. Lowe,2010年

11

域中不可变是否意味着它必须在数据库中不可变?例如,考虑以下假设,假设客户始终有一个地址:

customer.address = new Address('My Castle', 'Kings street');
customer_repo.save(customer);

现在,运行以下sql,考虑到客户id为1:

INSERT INTO addresses (customer_id, name, street)
VALUES (1, 'My Castle', 'Kings street');

现在对地址进行以下更改:

customer.address = new Address('Pauper palace', 'Outlands');
customer_repo.save(customer);

持久层非常聪明,它运行以下sql:

UPDATE addresses SET name='Pauper palance', street='Outlands'
WHERE customer_id = 1;

这样,您可以避免单独的DELETE AND INSERT语句的开销。我也认为某些RDBMS具有INSERT REPLACE之类的功能。MySql有REPLACE


+1我同意您的一般原则:我不明白为什么不变的域对象必须必然意味着不变的数据库行。
Andres F.

?在上述情况下,如果我们曾经需要更改地址类的城市属性对于给定的客户,我们将如何处理它,再假设客户可以有多个地址,因此AA一个客户和地址之间有很多关系舰
苏达

1
@Sudarshan这只是制作“不可变值对象”的一种方法,在数据库中并不是真正不可变的。出于性能原因。当然,每种情况都需要专门处理。我现在更喜欢为我的域使用事件搜索,但是我对此很上瘾。
andho 2012年

@Sudarshan专门回答您的问题,您只需运行一个更新查询即可将行更新为新值。如果是一对多,则这些地址将在“客户聚合根”中具有某种标识符,该标识符将用于在数据库中唯一标识它。然后,此标识符和customer_id将用于更新“地址”表中的该行。
Andho 2012年

@andho“这只是制作“不可变值对象”的一种方法,在数据库中并不是真正不变的。” 这真的让我很
高兴

6

在DDD中,不可变的对象几乎等同于值对象。这些对象不是实体,没有身份。因此,我总是将值对象持久化为它们所包含的实体的列(使用N / Hibernate,您可以使用组件)。他们没有自己的桌子。


4

这取决于不可变对象在数据库中的映射方式。如果它只是一个类似DateTime的组件(来自Joda Time库),则更改该值将导致更新而不是插入。但是,如果不可变变量更复杂,需要在表中一行,那么您就会遇到膨胀问题。

我想,尽管这是一个较弱的论点,但您可以通过这种方式实施审核跟踪。固定件的每次更改都可以通过插入件进行跟踪。尽管有许多更好的方法可以做到这一点。

总而言之,拥有不变的域对象(而不只是它们的组件)似乎与持久性有些不匹配。


一点也不。组件非常有用,因为它们允许域对象与相同的基础数据以不同的方式构成。问题在于强制不变性。就个人而言,我会将域对象视为可变的(主键字段除外),并使其保持简单。
加里·罗

“总而言之,拥有不变的域对象(而不只是它们的组件)似乎与持久性有些不匹配。” 在您看来,我们需要在哪里拥有不应建模为组件的价值对象(Hibernate术语)?---对不起,我输错了我的最后一条评论,因此删除了它。
Sudarshan

当单个实体完全由一行表示并且任何数据都不由另一个域对象共享时,请避免使用组件。
2013年

4

这取决于域。DDD没有指定编程范例是面向对象的。但是,面向对象的范例非常适合必须保留在数据库中的典型应用程序。

DDD仅声明您应该围绕表示该软件试图解决的实际问题的域模型来构建软件。如果该问题本质上是数学的,那么使用功能编程和不可变数据结构实现域层将很有意义。

另一方面,如果问题更多是典型的企业应用程序,并且您对所有域对象都使用不可变的对象结构,那么我认为您没有遵循DDD。我可以提出至少两个参数:

  • 您的实现不代表问题域的域模型。在这种情况下,您的问题域由状态需要修改的实体组成。那不是您实现它的方式。

  • 您的语言并不普遍。域模型中的语言和概念不遵循域专家的使用。

注意:DDD确实在适当的地方使用不可变的对象,它们仅称为值对象。

因此,我并不是说您不能使用纯功能数据结构创建数据库应用程序,也不是说您不应这样做。我只是认为您不能将其称为DDD,具体取决于应用程序的类型


彼得,好答案。您能在这里查看我的问题stackoverflow.com/questions/13783666/…并帮助我找出我的问题。我认为不可变的价值对象非常适合与企业无关的软件。我认为大多数企业应用程序的对象都是可变的。想象一下,拥有一个不变的深层嵌套值对象... ContactInfo-> PhysicalLocation-> Address,并且您想更新邮政编码...在我看来,创建整个对象图似乎是敌基督,而不仅仅是反模式。你怎么看?
Pepito Fernandez

3

如果使用的是ORM(例如Hibernate / NHibernate),则可以将层叠选项设置为自动删除孤立对象。如果一个人有一个值对象“地址”,则当您更改地址时,新地址将被保存,而旧地址将被删除,因为它现在是孤立的。


0

值对象在DDD中几乎总是不可变的,并且flyweight模式可用于将值对象的重复复制保留在内存中,就像.NET对字符串所做的一样。主要的权衡一方面是记忆,另一方面是创造效率。使用flyweight模式,必须进行基于值的比较以确定在flyweight缓存中是否已经存在任何新的或重构的值对象,但是由于强制了单个实例,因此通常可以通过引用安全地完成此之后的任何其他比较。无论是否使用flyweight,值对象仍然是不可变的,因为它们仅具有内在身份,因此更改其值将更改其身份。


-1

还有另一种使用不可变对象的方法...

您不必存储值5.0、6.0、7.0、8.0并每次都复制整个记录,而可以存储以下内容:5.0,+ 1.0,+ 1.0,+ 1.0,然后正确构建依赖项以重建序列5.0 ,6.0、7.0、8.0。这样,小的修改仅占用少量的内存...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.