如何建模价值对象关系?
上下文:
我有一本实体书。一本书可以有一个或多个描述。描述是值对象。
问题:
一个描述可以比另一个描述更具体。例如,如果描述包含书籍的内容以及封面的外观,则它比仅讨论封面的外观的描述更具体。我不知道如何对此进行建模以及如何让存储库保存它。了解这些关系既不是本书的责任,也不是本书的描述的责任。其他一些对象可以处理这个问题,然后要求存储库保存关系。但是 BookRepository.addMoreSpecificDescription(Description, MoreSpecificDescription) 存储库似乎很难保存。
DDD 中如何处理这样的事情?
context:
I have an entity Book. A book can have one or more Descriptions. Descriptions are value objects.
problem:
A description can be more specific than another description. Eg if a description contains the content of the book and how the cover looks it is more specific than a description that only discusses how the cover looks. I don't know how to model this and how to have the repository save it. It is not the responsibility of the book nor of the book description to know these relationships. Some other object can handle this and then ask the repository to save the relationships. But BookRepository.addMoreSpecificDescription(Description, MoreSpecificDescription) seems difficult for the repository to save.
How is such a thing handled in DDD?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
另外两个答案是一个方向(+1 顺便说一句)。我是在您编辑原始问题后进来的,所以这是我的两分钱...
我将值对象定义为具有两个或多个属性的对象,这些属性可以(并且)在其他实体之间共享。它们只能在单个聚合根内共享,这也很好。事实上,它们可以(并且正在)共享。
要使用您的示例,您将“描述”定义为值对象。这告诉我,具有多个属性的“描述”可以在多本书之间共享。在现实世界中,这是没有意义的,因为我们都知道每本书都有由作者或出版该书的大师撰写的独特描述。呵呵。因此,我认为描述并不是真正的值对象,但它们本身是图书聚合根实体边界中的附加实体对象(您可以在单个聚合根的实体中拥有多个实体)。即使是重新发行的书籍、较新的修订版等,对这一细微变化的描述也略有不同。
我相信这回答了您的问题 - 制作描述实体对象并在您的主图书实体聚合根后面保护它们(例如 Book.GetDescriptions()...)。这个答案的其余部分讨论了我如何处理存储库中的值对象,对于阅读这篇文章的其他人...
为了在存储库中存储值对象并检索它们,我们开始侵入我在切换时与自己搏斗的同一领域从“数据库优先”建模方法到 DDD 方法。我自己也曾研究过这个问题,即如何在数据库中存储值对象,并在没有身份的情况下检索它。直到我退后一步,意识到我在做什么……
在域驱动设计中,您正在对域中的值对象进行建模,而不是对数据存储进行建模。这是关键词。这意味着您没有将值对象设计为作为独立对象存储在数据存储中,您可以按照自己喜欢的方式存储它们!
让我们以值对象的常见 DDD 示例为例,即 Address()。 DDD 认为邮寄地址是完美的值对象示例,因为值对象的定义是一个对象,该对象的属性总和创建了该对象的唯一性。如果属性发生变化,它将是一个不同的值对象。并且相同的值对象(9teh其属性的总和)可以在其他实体之间共享。
邮寄地址是一个位置,即地球上特定位置的经度/纬度。多个人可以居住在该地址,当有人搬家时,占据同一邮寄地址的新人现在使用相同的值对象。
因此,我有一个 Person() 对象和一个 MailingAddress() 对象,其中包含地址信息。它通过 get/update/create 方法/服务受到我的 Person() 聚合根的保护。
现在,我们如何存储它并在同一家庭的成员之间共享它?啊,这就是 DDD——你并不是直接从 DDD 中对数据存储进行建模(尽管这样就很好了)。话虽如此,您只需创建一个表来显示您的 Person 对象,并且其中包含您的邮寄地址的列。存储库的工作是从数据存储中将信息重新水化回 Person() 和 MailingAddress() 对象,并在创建/更新操作期间将其拆分。
是的,您的数据存储中现在有重复的数据。具有相同邮寄地址的三个 Person() 实体现在都拥有该值对象数据的三个独立副本 - 这没关系!值对象应该很容易被复制和销毁。 “复制”是 DDD 手册中最恰当的词。
总而言之,域驱动设计是对域进行建模以表示对象的实际业务使用。您可以分别对 Person() 实体和 MailingAddress 值对象进行建模,因为它们在应用程序中的表示方式不同。您将它们保存为复制的数据,即与您的“人员”表相同的表中的附加列。
以上都是严格的DDD。但是,DDD 只是“建议”,而不是遵循的规则。这就是为什么你可以自由地做我自己和许多其他人已经做过的事情,一种松散的 DDD 风格。如果您不喜欢复制的数据,唯一的选择是您可以为 MailingAddress() 创建一个单独的表并在其上粘贴一个 Identity 列,然后更新您的 MailingAddress() 对象以使其现在具有该身份 -知道您只使用该身份将其链接到共享它的其他 Person() 对象(我个人喜欢第三个多对多关系表,以保持查询速度)。如果可能的话,您可以屏蔽该身份(即内部修饰符),以免暴露在聚合根/域之外,因此其他层(例如应用程序或 UI)不知道 MailingAddress 的身份列。另外,我会为 MailingAddress 创建一个专用存储库,并使用 PersonService 层将它们组合到正确的对象 Person.MailingAddress() 中。
抱歉,我的咆哮...:)
The other two answers are one direction (+1 btw). I am coming in after your edit to the original question, so here are my two cents...
I define a Value Object as an object with two or more properties that can (and is) shared amongst other entities. They can be shared only within a single Aggregate Root, that's fine too. Just the fact that they can (and are) shared.
To use your example, you define a "Description" as a Value Object. That tells me that "Description" with multiple properties can be shared amongst several Books. In the real-world, that does not make sense as we all know each book has unique descriptions written by the master of who authored or published the book. Hehe. So, I would argue that Descriptions aren't really Value Objects, but themselves are additional Entity objects within your Book Aggregate Root Entity boundery (you can have multiple entities within a single aggregate root's entity). Even books that are re-released, a newer revision, etc have slightly different descriptions describing that slight change.
I believe that answers your question - make the descriptions entity objects and protect them behind your main Book Entity Aggregate Root (e.g. Book.GetDescriptions()...). The rest of this answer addresses how I handle Value Objects in Repositories, for others reading this post...
For storing Value Objects in a repository, and retrieving them, we start to encroach onto the same territory I wrestled with myself when I went switched from a "Database-first" modeling approach to a DDD approach. I myself wreslted with this one, on how to store a Value Object in the DB, and retrieve it without an Identity. Until I stepped back and realized what i was doing...
In Domain Driven Design, you are modeling the Value Objects in your domain - not your data store. That is the key phrase. It means you are not designing the Value Objects to be stored as independant objects in the data store, you can store them however you like!
Let's take the common DDD example of Value Objects, that being an Address(). DDD presents that an Mailing Address is the perfect Value Object example, as the definition of a Value Object is an object of who's properties sum up to create the uniqueness of the object. If a property changes, it will be a different Value Object. And the same Value Object 9teh sum of its properties) can be shared amongst other Entities.
A Mailing Address is a location, a long/lat of a specific location on the planet. Multiple people can live at the address, and when someone moves, the new people to occupy the same Mailing Address now use the same Value Object.
So, I have a Person() object with a MailingAddress() object that has the address information in it. It is protected behind my Person() aggregate root with get/update/create methods/services.
Now, how do we store that and share it amongst the people in the same household? Ah, there lies DDD - you aren't modeling your data store straight from your DDD (even though, that would be nice). With that said, you simple create a single Table that presents your Person object, and it has the columns for your mailing address within it. It is the job of your Repository to re-hydrate that information back into your Person() and MailingAddress() object from the data store, and to split it up during the Create/Update operations.
Yep, you'd have duplicate data now in your data store. Three Person() entities with the same mailing address all now have three seperate copies of that Value Object data - and that is ok! Value Objects are meant to be copied and destoyed quite easily. "Copy" is the optimum word there in the DDD playbook.
So to sum up, Domain Drive Design is about modeling your Domain to represent your actual business use of the objects. You model a Person() entity and a MailingAddress Value Object seperately, as they are represented differently in your application. You persist them a copied-data, that being additional columns in the same table as your Person table.
All of the above is strict-DDD. But, DDD is meant to be just "suggestions", not rules to live by. That's why you are free to do what myself and many others have done, kind of a loose-DDD style. If you don't like the copied data, your only option is that being you can create a seperate table for MailingAddress() and stick an Identity column on it, and update your MailingAddress() object to have now have that identity on it - knowing you only use that identity to link it to other Person() objects that share it (I personally like a 3rd many-to-many relationship table, to keep the speed of the queries up). You would mask that Idenity (i.e. internal modifier) from being exposed outside of your Aggregate Root/Domain, so other layers (such as the Application or UI) do not know of the Identity column of the MailingAddress, if possible. Also, I would create a dedicated Repository just for MailingAddress, and use your PersonService layer to combine them into the correct object, Person.MailingAddress().
Sorry for the rant... :)
首先,我认为评论应该是实体。
其次,为什么要尝试对评论之间的关系进行建模?我不认为他们之间有天然的关系。 “更具体”对于建立关系来说太模糊了。
如果您在模拟这种情况时遇到困难,则表明可能不存在任何关系。
First, I think that reviews should be entities.
Second, why are you trying to model relationships between reviews? I don't see a natural relationship between them. "More specific than" is too vague to be useful as a relationship.
If you're having difficulty modeling the situation, that suggests that maybe there is no relationship.
我同意杰森的观点。我不知道你让评论有价值的理由是什么。
我希望 BookReview 具有 BookReviewContentItems,以便您可以在 BookReview 上调用一个方法来决定它是否足够具体,该方法根据查询其内容项集合来决定。
I agree with Jason. I don't know what your rationale is for making reviews value objects.
I would expect a BookReview to have BookReviewContentItems so that you could have a method on the BookReview to call to decide if it is specific enough, where the method decides based on querying its collection of content items.