2012-05-23 74 views
11

我在處理相當複雜的場景時遇到了一些困難。我看到了很多類似的問題,但沒有一個能夠滿足我的需求。DDD - 聚合內子對象的修改

訂單(聚合根)使用多個OrderLines(子實體)創建。根據業務規則,每個OrderLine必須在訂單的生命週期中保持相同的身份。 OrderLines有很多(20+)屬性,並且在訂單被視爲「鎖定」之前可以經常發生突變。另外,還有一些不變量必須在根層次上執行;例如,每個訂單行都有一個數量,訂單的總數量不能超過X.

我不確定如何在考慮對OrderLines進行更改時對此業務情景建模。我有4個可以想象的選擇,但沒有一個看起來令人滿意:

1)當需要修改OrderLine時,請使用根提供的參考。但是我失去了檢查根中的不變邏輯的能力。

var orderLine = order.GetOrderLine(id); 
orderLine.Quantity = 6; 

2)調用訂單上的方法。我可以將所有不變的邏輯,但後來我堅持用的方法增殖修改訂單行的許多特性:

order.UpdateOrderLineQuantity(id, 6); 
order.UpdateOrderLineDescription(id, description); 
order.UpdateOrderLineProduct(id, product); 
... 

3)如果我處理的訂單行的值對象,這可能是更容易,但它必須保持每個業務需求的相同身份。

4)我可以獲取對OrderLines的引用,以進行不影響不變量的修改,並通過訂單處理。但是,如果不變量受到大多數OrderLine屬性的影響呢?這種反對是假設的,因爲只有少數屬性會影響不變量,但是隨着我們發現更多的業務邏輯,這個屬性可能會發生變化。

任何建議表示讚賞...不要猶豫,讓我知道如果我密集。

回答

5
  1. 不是最優的,因爲它允許打破域不變。

  2. 將導致代碼重複和不必要的方法爆炸。

  3. 與1)相同。使用Value對象不會對維護域不變性有所幫助。

  4. 這個選項是我想要的。我也不會擔心潛在的和假想的變化,直到他們成爲現實。設計將隨着您對域的理解而發展,並且可以在以後進行重構。爲了避免將來可能發生的變化,確實沒有任何阻礙您現有設計的價值。

+0

謝謝你的回答 - 我認爲這可能是我提出的最佳選擇。我實際上希望有人提出一個我可能忽略的更好的模式;)我對使用它有點懷疑,因爲它不乾淨。或者,也許更好的方法是......不一致,就像@eulerfx指出的那樣。但我想現在會做... – Cork

+2

我知道我已經接受這個答案了......但我想到了一種不同的方法。在訂單上有一個Save(IOrderLine)方法可以嗎?然後我可以傳遞給訂單,避免一堆細化的方法,並且仍然允許訂單執行不變量。 – Cork

5

與2相反,4的一個缺點是缺乏一致性。在某些情況下,保持一定程度的關於更新訂單行項目的一致性可能是有益的。可能不會立即明白爲什麼通過訂單完成某些更新,而通過訂單行項目完成其他更新。此外,如果訂單行有20多個屬性,那麼這可能表示這些屬性之間可能會進行分組,從而導致訂單行上的屬性更少。總體而言,只要您確保操作原子化,一致並符合無處不在的語言,方法2或4都可以。

+1

感謝您的答案!我同意缺乏一致性,但我認爲這是我最好的選擇。實際上,我們開始使用訂單中的很多訂單行屬性,但隨着我們繼續分析域,我們發現訂單行是一個更合適的位置。然而,我們有可能會被帶走一點...... – Cork

5

還有第五種方法做到這一點。您可以觸發domain event(例如QuantityUpdatedEvent(order, product, amount))。通過遍歷訂單行列表,讓內部彙總在訂單行列表中進行處理,選擇匹配產品並更新其數量(或將代理操作委託給OrderLine甚至更​​好)

+1

當一個實體的孩子可以用多種不同的方式進行修改時,我同意域名事件通常是正確的方式。吉米Bogard也寫了一些很好的作品[這裏](http://lostechies.com/jimmybogard/2010/04/08/strengthening-your-domain-domain-events/)和[這裏](http:// lostechies。 COM/jimmybogard/2014/05/13/A-更好域事件模式/)。 –

4

域事件是最穩健的解決方案。

但是,如果這太過分了,您還可以使用參數對象模式對#2進行變化 - 在實體根上只有一個ModfiyOrderItem函數。提交一個新的,更新的訂單項目,然後在內部訂單驗證這個新對象並進行更新。

所以你典型的工作流程會變成類似

var orderItemToModify = order.GetOrderItem(id); 
orderItemToModify.Quantity = newQuant; 

var result = order.ModifyOrderItem(orderItemToModfiy); 
if(result == SUCCESS) 
{ 
    //good 
} 
else 
{ 
    var reason = result.Message; etc 
} 

主要缺點這裏是它允許程序員修改的項目,而不是承諾,並沒有意識到這一點。但它很容易擴展和測試。

1

如果您的項目很小,並且您希望避免域事件的複雜性,那麼還有另一種選擇。創建處理規則秩序服務,並通過它來對訂單行的方法:

public void UpdateQuantity(int quantity, IOrderValidator orderValidator) 
{ 
    if(orderValidator.CanUpdateQuantity(this, quantity)) 
     Quantity = quantity; 
} 

CanUpdateQuantity以當前訂單行和新的數量作爲參數。它應查找訂單並確定更新是否會導致總訂單數量發生違規。 (您將必須確定如何處理更新違規。)

如果您的項目很小,而且您不需要域事件的複雜性,這可能是一個很好的解決方案。

這種技術的一個缺點是您要將Order的驗證服務傳遞到OrderLine中,但它並不屬於它。相比之下,引發域事件會將Order邏輯從OrderLine中移出。然後OrderLine可以對世界說:「嘿,我正在改變我的數量。」而訂單驗證邏輯可以在處理程序中進行。