koe*_*oen 1 domain-driven-design repository value-objects object-relationships
上下文:
我有一个实体书.一本书可以有一个或多个描述.描述是价值对象.
问题:
描述可能比另一描述更具体.例如,如果描述包含书籍的内容以及封面的外观,则它比仅讨论封面外观的描述更具体.我不知道如何建模以及如何让存储库保存它.了解这些关系并不是本书和书籍描述的责任.其他一些对象可以处理此问题,然后请求存储库保存关系.但是BookRepository.addMoreSpecificDescription(Description,MoreSpecificDescription)似乎很难保存.
在DDD中如何处理这样的事情?
另外两个答案是一个方向(+1 btw).在你编辑原始问题后我进来了,所以这是我的两分钱......
我将一个Value Object定义为一个具有两个或多个属性的对象,这些属性可以(并且是)在其他实体之间共享.它们只能在单个聚合根中共享,这也很好.只是他们可以(并且是)共享的事实.
要使用您的示例,请将"描述"定义为值对象.这告诉我,具有多个属性的"描述"可以在几本书之间共享.在现实世界中,这是没有意义的,因为我们都知道每本书都有由撰写或出版该书的大师所写的独特描述.呵呵.因此,我认为Descriptions不是真正的Value Objects,但它们本身是Book Aggregate Root Entity boundery中的其他Entity对象(您可以在单个聚合根的实体中拥有多个实体).即使是重新发布的书籍,更新的版本等也会略微不同地描述这种微小的变化.
我相信这会回答你的问题 - 将描述实体对象并保护在主要的Book Entity Aggregate Root(例如Book.GetDescriptions()...)后面.这个答案的其余部分解决了我如何处理存储库中的Value Objects,以及其他人阅读这篇文章......
为了将值对象存储在存储库中并检索它们,当我从"数据库优先"建模方法切换到DDD方法时,我们开始侵入我自己摔跤的相同区域.关于如何在数据库中存储值对象,并在没有标识的情况下检索它,我自己也对此进行了讨论.直到我退后一步,意识到我在做什么......
在域驱动设计中,您要对域中的值对象建模 - 而不是数据存储.这是关键词.这意味着您没有将Value Objects设计为存储为数据存储中的独立对象,您可以随意存储它们!
我们来看一下Value Objects的常见DDD示例,即Address().DDD表明邮件地址是完美的值对象示例,因为值对象的定义是谁的属性总结以创建对象的唯一性的对象.如果属性发生更改,则它将是不同的值对象.并且可以在其他实体之间共享相同的Value Object 9teh其属性的总和.
邮寄地址是一个位置,是地球上特定位置的长/纬.多人可以居住在该地址,当有人移动时,占用相同邮件地址的新人现在使用相同的值对象.
所以,我有一个带有MailingAddress()对象的Person()对象,该对象中包含地址信息.它使用get/update/create methods/services在我的Person()聚合根后面受到保护.
现在,我们如何存储并在同一家庭中的人们之间分享?啊,有DDD - 您不是直接从您的DDD建模您的数据存储(即使这样会很好).有了这个,你可以简单地创建一个表格来呈现你的Person对象,它有你邮寄地址的列.您的存储库的工作是将该信息重新水合回数据存储中的Person()和MailingAddress()对象,并在创建/更新操作期间将其拆分.
是的,您的数据存储中现在有重复的数据.具有相同邮件地址的三个Person()实体现在都具有该Value Object数据的三个单独副本 - 这没关系!价值对象意味着很容易被复制和去除."复制"是DDD剧本中的最佳单词.
总而言之,Domain Drive Design是关于对域进行建模以表示对象的实际业务用途.您可以单独建模Person()实体和MailingAddress Value Object,因为它们在您的应用程序中的表示方式不同.您将它们保留为复制数据,这是与Person表相同的表中的其他列.
以上所有都是严格的DDD.但是,DDD只是"建议",而不是生活规则.这就是为什么你可以自由地做我自己和许多其他人所做的事情,这是一种松散的DDD风格.如果您不喜欢复制的数据,您唯一的选择是,您可以为MailingAddress()创建一个单独的表并在其上粘贴一个Identity列,并更新您的MailingAddress()对象,使其现在具有该标识 - 知道你只使用那个标识将它链接到共享它的其他Person()对象(我个人喜欢第三个多对多关系表,以保持查询的速度).您将掩盖Idenity(即内部修饰符)暴露在聚合根/域之外,因此其他层(例如应用程序或UI)不知道MailingAddress的Identity列(如果可能).此外,我将为MailingAddress创建一个专用的存储库,并使用您的PersonService层将它们组合到正确的对象Person.MailingAddress()中.
抱歉咆哮...... :)