2011-01-29 73 views
5

在嘗試理解領域驅動設計時,我不斷回到一個我似乎無法明確回答的問題。邏輯何時屬於業務對象/實體,它何時屬於服務?

如何確定屬於某個域實體的邏輯以及屬於某個域服務的邏輯?

例子: 我們有一個在線商店的訂單類。這個類是一個實體和一個聚合根(它包含OrderItems)。

Public Class Order:IOrder 
{ 
    Private List<IOrderItem> OrderItems 

    Public Order(List<IOrderItem>) 
    { 
     OrderItems = List<IOrderItem> 
    } 

    Public Decimal CalculateTotalItemWeight() 
    //This logic seems to belong in the entity. 
    { 
     Decimal TotalWeight = 0 
     foreach(IOrderItem OrderItem in OrderItems) 
     { 
      TotalWeight += OrderItem.Weight 
     } 
     return TotalWeight 

    } 
} 

我想大多數人會同意CalculateTotalItemWeight屬於實體。但是,在某些時候,我們必須將此訂單運送給客戶。要做到這一點,我們需要做兩件事:

1)確定運送此訂單所需的郵資費率。

2)確定郵資費率後打印運輸標籤。

這兩項操作都需要依賴那些Order實體外,如外部Web服務來檢索郵政資費。我們應該如何完成這兩件事?我看到幾個選項:

1)直接在域實體中編碼邏輯,如CalculateTotalItemWeight。然後我們打電話給:

Order.GetPostageRate 
Order.PrintLabel 

2)將邏輯放入接受IOrder的服務中。然後,我們稱之爲:

PostageService.GetPostageRate(Order) 
PrintService.PrintLabel(Order) 

3)創建爲在訂單操作每個動作類,並通過構造函數注入傳遞類的實例,以訂單(這是選項1的變化,但允許重複使用在RateRetriever和LabelPrinter類):

Public Class Order:IOrder 
{ 
    Private List<IOrderItem> OrderItems 
    Private RateRetriever _Retriever 
    Private LabelPrinter _Printer 

    Public Order(List<IOrderItem>, RateRetriever Retriever, LabelPrinter Printer) 
    { 
     OrderItems = List<IOrderItem> 
     _Retriever = Retriever 
     _Printer = Printer 
    } 

    Public Decimal GetPostageRate 
    { 
     _Retriever.GetPostageRate(this) 
    } 

    Public void PrintLabel 
    { 
     _Printer.PrintLabel(this) 
    } 
} 

其中的這些方法,你選擇這個邏輯之一,如果有的話?你選擇的理由是什麼?最重要的是,是否有一套指導方針讓你選擇?

+1

訂單難言不可知關於其的OrderDetail項目......是你的意思? – 2011-01-29 23:43:23

回答

1

我傾向於有一個外部服務來確定運費。對我來說,這是應用程序邏輯,而不是順序特定的邏輯。例如,您可能會決定一段時間爲特定尺寸的訂單或特定的忠誠客戶羣提供免費送貨服務。對我而言,邏輯將傾向於獨立於訂單構建的方式而改變。

最有可能我會負責下訂單的代碼(某種訂購處理器服務,在應用層或命令處理程序中)交給服務以獲得運費,然後通過這個速度進入訂單,所以我想選項2.

對於打印運輸標籤,我傾向於讓域名沿着http://www.udidahan.com/2009/06/14/domain-events-salvation/的行提高事件。然後單獨的處理程序將處理打印標籤。同樣,這樣做的邏輯在於,您打印標籤的方式可能會因您構建訂單的方式而異,因此將其分開是有意義的。使用域事件似乎是確保在正確的時間打印標籤而不要求訂單(或訂單處理器)瞭解打印邏輯的最簡單方法。

0

我的觀點: 該域是什麼包含您的應用程序的邏輯,沒有基礎設施cruft。邏輯是,當確認訂單時打印標籤並確定運費。這應該在域中。

基礎架構然後完成域想要做的事情。該域可以通過消息或事件讓基礎設施知道。

這種方式沒有基礎設施泄漏到域中,只需要一種將消息傳輸到域外的方法。

+0

那麼你會使用我提到的方法之一,還是不同的? – 2011-01-30 00:47:34

1

我會使用(2)。

它不會增加您的訂單項目的複雜性。

對我來說,它似乎是幫手服務的自然使用。

更新:響應於評價:該wiki頁面狀態:

貧血域模型:使用這種 圖案,邏輯通常是 在單獨的類來實現這 變換域 的狀態物體

+0

在您看來,使用#2是否會將我們推向貧血域模型? – 2011-01-30 00:03:42

+0

我不這麼認爲,就我所瞭解的情況而言,只要所討論的對象沒有被改變或變形,讓一個單獨的對象帶上一堆其他對象並基於它們的狀態做出決定就沒有問題。 – 2011-01-30 00:18:13

1

如果要訪問外部Web服務得到郵資,最好是建立在應用層接口,因爲埃文本身建議,如果你想與外部Web服務交談,你應該建立在應用層界面,你會將服務實現注入到您的域對象中。對於打印運輸標籤,因爲只有郵資費率確定時纔打印標籤,所以是一種類似PostageRateConfirmed的事件您的域可以提高此事件。

http://danhaywood.com/2010/04/30/accessing-domain-services-from-entities/