2016-06-08 85 views
0

我有一個發票對象,其中包含項目,並且每個項目都與服務有關係。DDD子對象引用

以下結構。

{ 
    "invoiceId" : "dsr23343", 
    "items":{ 
     "id":1, 
     "service":{ 
     "serviceCode":"HTT" 
     } 
    } 
} 

我的要求之一是該項目不應與我們的系統中不存在的服務關係。 從我的理解,域對象不應該進入無效狀態。 那麼,我做的是以下幾點:

var service = new Service("SomeService"); 
var item = new Item(service); 
invoice.AddItem(item); 

我的問題是,我應該要求AddItem函數接收存儲庫作爲第二個參數,並拋出異常,如果服務不存在數據庫中?

+0

您是否考慮過使用ServiceFactory並在那裏驗證服務? – Cerad

+0

服務是一個聚合根嗎? – plalx

+0

您無需等到物品被添加到發票中。如果服務無效(在系統中不存在),創建服務對象本身應該失敗 – falcon

回答

1

我的問題是,我是否需要AddItem函數接收存儲庫作爲第二個參數,並拋出異常,如果服務不存在數據庫中?

簡答:當然,爲什麼不呢?

較長的答案...

如果服務和發票都是相同集合的一部分,那麼倉庫是不必要的 - 只是看總的狀態。所以接下來假定在發票和服務之間有一個事務邊界。

使用存儲庫作爲參數是有點太多的東西 - 發票並不需要加載的服務,它只是需要知道服務存在。因此,您可以使用支持「該服務是否存在?」的DomainService而不是將方法簽名放入方法簽名中。查詢。 (DomainService的實現可能在存儲庫中進行查找 - 我們在這裏沒有做到魔術,我們只是將發票與實現細節隔離開來,它不需要知道)。

在簽名文檔中使用更具限制性的接口,明確了這些組件之間的集成協議。

那就是說,要求是非常可疑的。如果服務和發票處於不同的集合中,那麼它們可能具有不同的生命週期。當您嘗試加載發票時應該會發生什麼,其中包含引用不再存在的服務的項目?這個用例是否應該爆炸?如果是這樣,將很難編輯發票來解決問題....

如果,當您將項目添加到發票,某個其他線程正在刪除服務...?

回顧Udi Dahan的散文:Race Conditions Don't Exist。執行摘要 - 如果您的模型對時間的微秒變化很敏感,那麼您可能不會對業務建模。

爲了保護這個「不變」,你至少有三種其他的選擇。

一個在客戶端;如果你不讓客戶產生無效的服務代碼,那麼你不會有這個問題。輸入驗證屬於客戶端組件或應用程序組件中,而不是模型。也就是說,當應用程序通過跨過流程邊界的DTO構造ServiceCode時,可能會檢查這種情況。

一個是模型的下游 - 如果您可以檢測到引用無效的服務代碼的發票項目,那麼您可以廣播一個例外報告,並使用應急響應流程來管理該問題。一致性問題很少見,易於檢測,易於修復,不需要域模型中的嚴格驗證。

一個在模型本身之內 - 如果發票項目的創建與服務的生命週期密切相關,那麼可能該項目是由服務創建的,而不是由發票創建。例如

class Service { 
    reportUsage(Customer, TimePeriod) 
} 

不會是一個不尋常的簽名看,你可能可以確信的是,服務養域事件是要正確地報告自己的ServiceCode。

+0

我喜歡你在客戶端驗證的解決方案,但是域對象將被允許進入無效狀態。我想我應該根據業務需求做出妥協。 – Robert

+0

@Robert你需要提出一個商業問題 - 如果我們爲不存在的服務添加一個項目會發生什麼。通常,如果你做這樣的操作,你從某個地方得到了這個「SomeService」字符串,所以可能它在你的命令形成的時候就存在了。這使得上述問題完全有效,並且不是「無效狀態」,只是表明您必須處理。還要計算出發生這種情況的可能性,指的是DDDx Greg Young對過度工程的討論。 –