OpenExpressApp使用的是CSLA進行類庫框架,進行類庫設計時可以借鑒DDD的領域驅動設計概念,本篇介紹一下其中的聚合概念,理解這個概念後能夠清晰的管理類庫之間的關系網,可以降低復雜系統的復雜性並提高可維護性。
聚合
一個模型會包含眾多的領域對象,不管在設計時做了多少考慮,我們都會看到許多對象會跟其他的對象發生關聯,形成了一個復雜的關系網,如果設計不好時,這個對象網會帶來不好的維護性和低性能。
聚合是一個用來定義對象所有權和邊界的領域模式,它使用邊界將內部和外部的對象劃分開來,針對數據變化考慮的一系列相關的對象組合。聚合通過定義清晰的所有權和邊界使模型變得更緊湊,避免出現盤根錯節的對象(關系)網,它可以減少對象間不必要的聯系,使關系密切的對象聯系在一起,提高系統的管理。
聚合類對調用者隱藏了協作類的用法,可用來封裝領域類中復雜的、有侵入性的、狀態依賴的需求。
規則
每個聚合有一個根對象,它是個實體
外部對象只能訪問根對象,如果要訪問聚合子對象也必須通過根對象導航
根對象和子對象可以保持對其它任意聚合根對象的引用
當聚合的根建立時,所有聚合包含的對象將隨之建立,所有的不變量得到了強化。
保持數據一致性和強化不變量
其他對象只能持有根對象的引用,而不能直接修改聚合子對象。如果要更改子對象,它們只能通過根對象來執行某些操作。
根對象能夠變更其他的對象,但這是聚合內包含的操作,並且它是可控的
如果根從內存中被刪除或者移除,聚合內的其他所有的對象也將被刪除,因為再不會有其他的對象持有它們當中的任何一個了。當針對根對象的修改間接影響到聚合內的其他的對象,強化不變量變得簡單了,因為根將做這件事情。如果外部對象能直接訪問內部對象並且變更它們時,這將變得很難管理這些對象的一致性和不變量性
外部對象如何引用內部對象
通過根對象導航
臨時使用時,根對象可以將內部的臨時引用傳遞給外部對象,但作為限制,當操作完成後,外部對象不能再持有這個引用。一個簡單的實現方式是向外部對象傳遞一個值對象的拷貝。在這個對象上發生了什麼將不再重要,因為它不會以任何方式影響到聚合的一致性。
可變根對象
有時候一個對象在某個時刻是聚合根對象,在另一個時刻是另一個聚合子對象。一般這類情況會出現在資源類信息中,
材料類別下面有很多材料,這時材料類別+材料為一個聚合,材料為子對象。
入庫單明細入庫一個材料,這時入庫單+入庫單明細為一個聚合,材料為外部引用的另一個聚合根對象
上面例子表明了材料資源有時可以作為根對象,有時可以作為子對象來進行設計,在CSLA2008書中提到過一個可切換的根對象也就是處理這種情況。但是出現這種情況後,我總感覺有點別扭,寫起代碼來也總覺得不舒服。我的一個建議做法如下:
如果內部對象只是作為外部對象的一個只讀參考引用,可以通過以下方式獲取子對象,這樣材料對象就只是一個聚合對象中的子對象,而不需要作為可切換根對象了。