值与实体对象(域驱动设计)
我刚刚开始阅读DDD。我无法完全掌握"实体与价值"对象的概念。有人可以解释将"价值"对象设计为"实体"对象时系统可能会遇到的问题(可维护性,性能等)吗?例子太棒了...
解决方案
回答
归结为本质上的区别,身份对实体很重要,但对价值对象并不重要。例如,某人的姓名是一个值对象。客户实体可能由客户名称(值对象),List <Order> OrderHistory(实体列表)以及默认地址(通常是值对象)组成。客户实体将有一个ID,每个订单将有一个ID,但名称不应该;通常,无论如何在对象模型中,地址的身份可能无关紧要。
值对象通常可以表示为不可变对象。更改值对象的一个属性实质上会破坏旧对象并创建一个新对象,因为我们不像身份那样关注内容。正确地,只要对象的属性与另一个实例的属性相同,则Name上的Equals实例方法将返回" true"。
但是,更改诸如客户之类的实体的某些属性不会破坏客户。客户实体通常是可变的。身份保持不变(至少在持久保留对象之后)。
我们可能在没有意识到的情况下创建了价值对象。每当我们通过创建细粒度的类来表示实体的某个方面时,我们都会获得一个值对象。例如,对有效值有一些约束但由更简单的数据类型组成的IPAddress类将是一个值对象。 EmailAddress可以是字符串,也可以是具有自己的一组行为的值对象。
即使在数据库中具有标识的项目也很有可能在对象模型中也没有标识。但是最简单的情况是将一些有意义的属性组合在一起。当我们可以将Customer.FirstName,Customer.LastName,Customer.MiddleInitial和Customer.Title一起组合为Customer.Name时,我们可能不希望使用它们。当我们考虑持久性时,它们可能会成为数据库中的多个字段,但是对象模型不在乎。
回答
我不知道以下说法是否正确,但是我想说的是,在使用地址对象的情况下,我们希望将其用作值对象而不是实体,因为对实体的更改将反映在所有链接的对象上(例如一个人)。
以这种情况为例:我们与其他人一起住在房子里。如果我们将实体用于地址,我会争辩说所有个人对象都链接到一个唯一的地址。如果有人搬出,我们想更新他的地址。如果我们要更新地址实体的属性,则所有人将拥有不同的地址。对于值对象,我们将无法编辑该地址(因为它是不可变的),因此我们将被迫为该人员提供新的地址。
听起来对吗?我必须说,在阅读DDD书之后,我还是对这种差异感到困惑。
更进一步,该如何在数据库中建模?我们是否将"地址"对象的所有属性都作为"人"表中的列,还是要创建一个单独的"地址"表,该表也具有唯一的标识符?在后一种情况下,居住在同一所房屋中的人每个人都有一个地址对象的不同实例,但是这些对象除了其ID属性外都是相同的。
回答
由其所有属性共同定义的任何对象都是值对象。如果任何属性发生更改,我们将拥有一个值对象的新实例。这就是为什么将值对象定义为不可变的原因。
如果对象没有完全由其所有属性定义,则存在组成对象身份的属性子集。其余属性可以更改,而无需重新定义对象。不能将这种对象定义为不可变的。
进行区分的一种更简单的方法是将值对象视为永不更改的静态数据,将实体视为在应用程序中演化的数据。
回答
我在另一个线程中询问了这个问题,但我仍然感到困惑。我可能将性能考虑因素与数据建模混淆了。在我们的目录应用程序中,客户直到需要时才改变。这听起来有些愚蠢,但是客户数据的"读取"次数远远超过了"写入"次数,并且由于许多Web请求都击中了"活动集"对象,因此我不想一次又一次地加载客户。因此,我走了一条不变的路,将Customer对象加载,缓存并为希望见客户的99%(多线程)请求提供相同的请求。然后,当客户进行更改时,请一位"编辑"创建新客户并使旧客户无效。
我担心的是,如果许多线程看到同一个客户对象并且是可变的,那么当一个线程开始更改时,其他线程就会出现混乱。
我现在的问题是,1)这是否合理,以及2)如何最好地做到这一点而又无需复制有关该属性的大量代码。
回答
地址可以是取决于业务流程的实体或者值对象。地址对象可以是快递服务应用程序中的实体,而地址可以是某些其他应用程序中的值对象。在快递应用程序中,身份对于地址对象很重要