2017-05-17 83 views
0

當我需要調用某種業務方法時,即使操作與下面給出的操作一樣原始(僅將項添加到集合中),我也需要獲取與該操作相關的所有聚合根。我錯過了什麼?或者是基於CRUD的方法,其中運行單個查詢,包括表連接,最終選擇和插入 - 數據庫引擎爲您完成所有工作 - 在性能方面實際上更好?如何使用DDD高效地查詢多個聚合?

在下面的代碼中,我需要查詢單獨的聚合根(它創建另一個數據庫連接併發送另一個select查詢)。在真實世界的應用程序中,我一直在查詢大量多個單一聚合,對於單個業務活動最多隻能查詢8個。我該如何提高性能/查詢開銷?

域聚合根:

class Device 
{ 
    Set<ParameterId> parameters; 

    void AddParameter(Parameter parameter) 
    { 
     parameters.Add(parameter.Id); 
    } 
} 

class Parameter 
{ 
    ParameterId Id { get; } 
} 

應用層:

class DeviceApplication 
{ 
    private DeviceRepository _deviceRepo; 
    private ParameterRepository _parameterRepo; 

    void AddParameterToDevice(string deviceId, string parameterId) 
    { 
     var aParameterId = new ParameterId(parameterId); 
     var aDeviceId = new DeviceId(deviceId); 

     var parameter = _parameterRepo.FindById(aParameterId); 
     if (parameter == null) throw; 

     var device = _deviceRepo.FindById(aDeviceId); 
     if (device == null) throw; 

     device.AddParameter(parameter); 
     _deviceRepo.Save(device); 
    } 
} 

可能的解決方法

我已經告訴你可以通過這樣的另一個聚集的只是一個編號:

class Device 
{ 
    void AddParameter(ParameterId parameterId) 
    { 
     parameters.Add(parameterId); 
    } 
} 

但海事組織打破封裝(明確強調業務中的術語ID),也不防止粘貼錯誤或其他不正確的身份(由用戶創建)。

而Vaughn Vernon給出了使用第一種方法(傳遞整個聚合實例)的應用程序服務的示例。

+0

我可以直接添加/刪除IDS,而不是自己聚合? – EwanCoder

回答

2

簡短的答案是 - 根本不要查詢您的聚合。

聚合是暴露行爲而不是數據的模型。一般來說,它被認爲是一個代碼嗅覺,讓聚集上的getter(ID是例外)。這使得查詢有點棘手。

從廣義上講,有兩種相關的方法可以解決這個問題。有可能更多,但至少這些不會打破封裝。

選項1:使用域事件 - 通過讓您的域(聚合根)發出說明內部狀態更改的事件,您可以在專用於查詢的數據庫中構建表。完成之後,您將擁有高性能,非規範化的可查詢數據,必要時可以線性縮放。這使得查詢非常簡單。我對這個博客帖子這樣一個例子:How to Build a Master-Details View when using CQRS and Event Sourcing

選項2:推斷查詢表 - 我選項1一個巨大的風扇,但如果你沒有一個事件來源的方法,你仍然需要堅持在某個時刻你的聚合狀態。有各種各樣的方法可以做到這一點,但是您可以爲您的聚合插入持久性管道,從而將可查詢數據提取到讀取模型中用於查詢。

我希望這是有道理的。

+0

我使用域事件和單獨的讀取模型/ CQRS模式。但爲了執行DOMAIN操作,我需要查詢這些聚合以獲取其ID。還是你建議業務方法應該採用ID而不是聚合根? (儘管我沒有使用事件驅動,所以我保存域對象狀態,我需要查詢那些,而不是一些可能最終一致的讀取模型)。 – EwanCoder

+0

是的 - 您應該已經從閱讀模型中知道id。 – Codescribler

+0

所以當我需要添加一些參數到設備時,設備必須有一個方法接受ParameterId,而不是參數本身?這不是沃恩弗農如何做到的。目前我的設備有一個接受參數本身的方法,並在其中請求Id並保存它。 – EwanCoder

0

如果您發現在這種情況下使用連接進行RDBMS查詢可能會起作用 - 可能是您的聚合邊界錯誤。

例如 - 爲什麼需要加載Parameter才能將其添加到Device?您已擁有此Parameter的身份,您只需將此ID添加到Device中的引用Parameters的列表中即可。如果你這樣做是爲了滿足你的ORM - 你很可能做錯了什麼。

還記得你的彙總的交易界限。你真的想要在一個事務和一個連接內完成所有的數據庫操作。

+0

是的,你很正確。我有一個ID,我可以將此ID附加到ID列表中。只是我的AddParameter方法接受了參數聚集根(不是ParameterId值對象),以支持聚合之間的封裝和緊密內聚。這導致需要組裝這個其他聚合體(之後只是爲了獲得它的ID)。 – EwanCoder

+1

您可以使用值對象作爲ID來充分封裝,因此您將傳遞'ParameterId'對象(結構體),但這是完全有效的,您不必傳遞預先獲取的聚合體。很明顯,聚合從來沒有任何其他類型的引用,而不僅僅是一個id。 –

+0

封裝我的意思是業務邏輯(將參數傳遞給設備,而不是參數設備)。所有類型的ID都是封裝中斷的一小部分,因爲業務不關心Ids。而且我已經在使用ID對象的值對象。無論如何,如果我們通過'ParameterId'對象 - 還有另外一個問題:如果用戶將創建隨機的ParameterId對象或無效的ParameterId對象或已刪除的聚合的某個Id?當你傳遞一個聚合本身時,這會被自動阻止。 – EwanCoder