2009-07-17 77 views
10

如果人們認爲這已被毆打致死,我很抱歉。我剛剛花了幾個小時搜索和閱讀許多優秀的帖子,但我仍然困惑。DDD,存儲庫和封裝

我的困惑的來源是DTO與DDD和存儲庫。我希望我的POCO域對象具有智能,並且我想從存儲庫中獲取它們。但是,似乎我必須違反一些封裝規則才能使其發揮作用,並且似乎可以將DTO變成他們的頭像。

下面是一個簡單的例子:在我們的目錄應用程序中,零件可能是一個包含許多其他零件的包。因此,Part POCO有一個'GetChildren()'方法是有意義的,該方法返回IEnumerable < Part>。它甚至可能在列表中列出其他東西。

但是該列表如何解決?好像倉庫是答案:

interface IPartRepository : IRepository<Part> 
{ 
    // Part LoadByID(int id); comes from IRepository<Part> 
    IEnumerable<Part> GetChildren(Part part); 
} 

而且

class Part 
{ 
    ... 
    public IEnumerable<Part> GetChildren() 
    { 
     // Might manipulate this list on the way out! 
     return partRepository.GetChildren(this); 
    } 
} 

所以,現在我的目錄,除了從存儲庫(正確地)裝載部分的消費者,還可以繞過某些部分封裝通過直接調用GetChildren(part)來實現邏輯。那不好嗎?

我讀到存儲庫應該提供POCO的,但是DTO的可以在層間傳輸數據。計算了很多零件屬性 - 例如,價格是根據複雜的定價規則計算的。價格甚至不在存儲庫中的DTO中 - 因此,將定價數據傳遞迴Web服務似乎需要DTO消耗該部分,而不是相反。

這已經太長了。我的頭在哪裏擰開?

回答

2

解決此問題的一種方法是將邏輯移入子部件本身 - 即更改類的語義,以便Part對象負責在與父對象關聯時對其進行轉換。

例如,如果一個Part的價格是依賴於它的父Part,可以在下列時間(至少)確定的價格:

  • 在結構中,如果父Part(和所有其他必要的數據)。

  • AttachToParent(Part parentPart)方法或響應事件,即,OnAttachedToParent(Part parentPart)

  • 當客戶端代碼需要時(即第一次訪問其Price屬性時)。


編輯:我原來的答覆(見下文)真的沒有在DDD的精神。它涉及到域對象的簡單容器,許多人認爲這是一種反模式(參見Anemic Domain Model)。

PartIPartRepository之間的附加層(我稱之爲IPartService)解決了這個問題:移動GetChildren(part)IPartServicePart刪除它,然後讓客戶端代碼調用IPartService得到Part對象和他們的孩子,而不是直接打開存儲庫。 Part類仍然有一個ChildParts(或Children)屬性 - 它只是不知道如何填充它本身。

顯然,這會產生額外的成本 - 如果您在大多數情況下不需要額外的業務邏輯,則最終可能會爲存儲庫調用編寫或生成大量傳遞代碼。

+0

有趣。但是我感到困惑的是'將GetChildren(part)移動到IPartService中,並將其從部分中移除「,然後'Part類仍然有一個Childparts屬性。如果部分因某種原因需要按摩其孩子會怎樣? – n8wrl 2009-07-17 15:40:13

0

這裏公式的缺失部分是Parts對象的行爲,以及您想如何使用聚合。你是否需要針對每個Part到第n次遞歸的個別子女工作,或者你是否只使用「根」Part(即那些沒有父母的),並且它是整個孩子?

具有含相當一般類型Parts兒童的名單似乎將不能表達你的域模型特別好,但你可以做到這一點,遞歸延遲加載每個孩子集合Part聚合根。但是,我仍然非常小心無限遞歸的可能性。關於你的第二個問題,DTO不能像在數據傳入和傳出應用層時那樣在層間傳輸數據。

如果您使用的是面向服務的體系結構(您提到web服務,但它可以是任何SOA),它們非常有用。您的服務將查詢您的存儲庫,執行任何額外的工作,然後將您的域對象映射到平面DTO中以發送回請求客戶端。 DTO應該是簡單的,不包含邏輯和應用程序功能,以便序列化。

在您的應用程序內部使用您的域對象,並在外部使用DTO。