另外兩個答案是一個方向(+1 btw)。我進入原始問題編輯後,所以這裏是我的兩個分...
我定義一個值對象作爲一個對象與兩個或更多的屬性,可以(和)在其他實體之間共享。它們只能在一個聚合根中共享,也沒關係。只是他們可以(而且)共享的事實。
要使用您的示例,請將「描述」定義爲值對象。這告訴我,具有多個屬性的「描述」可以在幾本書籍中共享。在現實世界中,這是沒有意義的,因爲我們都知道每本書都有由撰寫或出版這本書的主人寫的獨特描述。呵呵。因此,我認爲描述並不是真正的值對象,但它們本身就是書聚合根實體boundery中的附加實體對象(在一個聚合根實體中可以有多個實體)。即使是重新發布的書籍,更新版本等,也略有不同的描述來描述這種輕微的變化。
我相信你可以回答你的問題 - 使描述實體對象,並保護它們在你的主要書本實體聚集根(例如Book.GetDescriptions()...)之後。這個答案的其餘部分解決了我如何處理存儲庫中的值對象,其他讀這篇文章...
爲了在存儲庫中存儲值對象,並檢索它們,我們開始侵入我與自己搏鬥的同一領域當我從「數據庫優先」建模方法切換到DDD方法時。我自己就這個問題討論瞭如何在數據庫中存儲一個Value對象,並在沒有Identity的情況下檢索它。直到我退後一步,意識到我在做什麼......
在域驅動設計中,您正在爲域中的值對象建模 - 而不是您的數據存儲。這是關鍵短語。這意味着你並沒有將價值對象設計成獨立的對象存儲在數據存儲中,你可以存儲它們,只要你喜歡!
我們來看一個常見的值對象DDD示例,即Address()。 DDD表示郵寄地址是完美的價值對象示例,因爲值對象的定義是誰的屬性的對象總和來創建對象的唯一性。如果一個屬性發生變化,它將是一個不同的值對象。並且相同的Value Object 9teh的屬性總和)可以在其他實體之間共享。
郵寄地址是一個位置,是地球上特定位置的長/緯度。多個人可以住在地址上,當有人移動時,現在佔用相同郵寄地址的新人使用相同的值對象。
所以,我有一個Person()對象的MailingAddress()對象中有地址信息。它通過get/update/create methods/services保護在我的Person()聚合根後面。
現在,我們如何存儲並分享給同一家庭中的人?啊,DDD就在於 - 你不是直接從你的DDD建模你的數據存儲(儘管這很好)。這就是說,您可以簡單地創建一個呈現Person對象的單個表格,並在其中包含您的郵寄地址的列。您的Repository的工作是將這些信息從數據存儲重新提供給Person()和MailingAddress()對象,並在創建/更新操作期間將其分解。
是的,你現在在你的數據存儲中有重複的數據。具有相同郵寄地址的三個Person()實體現在都具有三個Value對象數據的單獨副本 - 這沒關係!值對象意味着很容易被複制和刪除。 「複製」是DDD劇本中的最佳單詞。
所以總結一下,Domain Drive Design是關於建模您的域以表示您對象的實際業務用途。您可以單獨對Person()實體和MailingAddress值對象建模,因爲它們在應用程序中以不同方式表示。你堅持他們一個複製的數據,這是在你的Person表的同一個表中的額外的列。
以上所有內容均嚴格爲DDD。但是,DDD意味着只是「建議」,而不是遵守規則。這就是爲什麼你可以自由地做我自己和其他人做的事情,這種寬鬆的DDD風格。如果你不喜歡複製的數據,唯一的選擇就是你可以爲MailingAddress()創建一個單獨的表,並在其上添加一個標識列,並更新你的MailingAddress()對象,使其具有該標識 - 知道你只使用該標識將其鏈接到其他共享它的Person()對象(我個人喜歡第三對多關係表,以保持查詢速度)。如果可能,您將屏蔽該Idenity(即內部修飾符)暴露在聚合根/域之外,以便其他層(如應用程序或UI)不知道MailingAddress的標識列。另外,我會爲MailingAddress創建一個專用的Repository,並使用PersonService層將它們合併到正確的對象Person.MailingAddress()中。
對不起,咆哮...... :)
注:我已經使用描述而不是審查來修改示例。我認爲這種關係並不自然。比方說,用戶喜歡看到顯示摘要的描述。然後任何描述顯示摘要(和更多)將適合。這就是我的意思是'比'更具體'。 – koen 2009-12-18 21:21:04