1 前言
實體是領域模型中的領域對象。
MVC開發人員總将關注點放在資料,而非領域。因為在軟體開發中,DB占據主導地位。首先考慮的是資料的屬性(即資料庫的列)和關聯關系(外鍵關聯),而不是富有行為的領域概念。
導緻将資料模型直接反映在對象模型,那些表示領域模型的實體(Entity)被包含了大量getter/setter。雖然在實體模型中加入getter/setter并非大錯, 但這不是DDD做法。
過于強調實體的作用卻忽視了值對象。受到DB和持久化架構影響,實體被濫用,于是開始讨論如何避免大範圍使用實體…
2 為什麼使用實體
當我們需要考慮一個對象的個性特征或需要區分不同對象時,就引入實體這個領域概念。
一個實體是一個唯一的東西,可在一段時間内持續變化。
這些對象重要的不是屬性,而是其延續性和辨別,會跨越甚至超出軟體生命周期。
也正是 唯一身份辨別和可變性(mutability) 特征将實體對象差別于值對象。
實體模組化并非總是完美。很多時候,一個領域概念應該模組化成值對象,而非實體。這意味着DDD開發CRUD軟體系統時可能更适用。但由于隻從資料出發,CRUD系統是不能建立出好的業務模型的。
在可以使用DDD時,我們會将資料模型轉變為實體模型。
通過辨別區分對象,而非屬性:
此時應将辨別作為主要的模型定義。同時保持簡單類定義,關注對象在生命周期中的連續性和唯一辨別性。不應該通過對象的狀态形式和曆史來區分不同實體……對于什麼是相同的東西,模型應該給出定義。
如何正确使用和設計實體呢?
3 唯一辨別
在實體設計早期,關注能展現實體身份唯一性的主要屬性和行為及如何查詢實體,忽略次要的屬性和行為。
設計實體時,首先考慮實體的本質特征,特别是實體的唯一辨別和對實體的查找,而不是一開始便關注實體的屬性和行為。隻有在對實體的本質特征有用的情況下,才加入相應的屬性和行為。
找到多種能夠實作唯一辨別性的方式,同時考慮如何在實體生命周期内維持唯一性。
實體的唯一辨別不見得一定有助對實體的查找和比對。将唯一辨別用于實體比對通常取決于辨別的可讀性。
比如:
若系統提供根據人名查找功能,但此時一個Person實體的唯一辨別可能不是人名,因為重名情況很多
若某系統提供根據公司稅号的查找功能,稅号便可作為Company實體的唯一辨別
值對象可用于存放實體的唯一辨別。值對象是不變(immutable)的,這就保證了實體身份的穩定性,并且與身份辨別相關的行為也可得到集中處理。便可避免将身份辨別相關的行為洩漏到模型的其他部分或用戶端中去。
3.1 建立實體身份辨別的政策
每種技術方案都存在副作用。比如将關系型DB用于對象持久化時,這樣的副作用将洩漏到領域模型。建立前需考慮辨別生成的時間、關系型資料的引用辨別和ORM在辨別建立過程中的作用等,還會考慮如何保證唯一辨別的穩定性。
3.2 辨別穩定性
絕大多數場景不應修改實體的唯一辨別,可在實體的整個生命周期中保持辨別的穩定性。
可通過一些簡單措施確定實體辨別不被修改。可将辨別的setter方法向使用者隐藏。也可在setter方法種添加邏輯以確定辨別在已存在時不再更新,比如可使用一些斷言:
username屬性是User實體的領域辨別,該屬性隻能進行一次修改,并且隻能在User對象内修改。setter方法setUsername實作了自封裝性且對用戶端不可見。當實體的public方法自委派給該setter方法時,該方法将檢查username屬性,看是否已被指派。若是,表明該User對象的領域辨別已經存在,程式将抛異常。

這個setter方法并不會阻礙Hibernate重建對象,因對象在建立時,它的屬性都是使用預設值,且采用無參構造器,是以username屬性的初始值為null。然後,Hibernate将調用setter方法,由于username屬性此時為null,該 setter方法得以正确地執行,username屬性也将被賦予正确的辨別值。