文檔目錄 本節內容: 實體類 聚合根類 領域事件 約定的介面 審計 軟刪除 活躍/消極 實體 實體變化事件 IEntity 介面 領域事件 審計 軟刪除 活躍/消極 實體 實體是DDD一個核心的概念。Eric Evans是這麼描述的:“一個對象根本上不是按它的特性定義的,而是按一個線程的連續性和身份 ...
本節內容:
實體是DDD一個核心的概念。Eric Evans是這麼描述的:“一個對象根本上不是按它的特性定義的,而是按一個線程的連續性和身份來定義”。所以實體有一個id屬性存入資料庫中。一個實體通常映射成關係型資料庫的一個表。
在ABP里,實體從Entity類上繼承,示例代碼如下:
public class Person : Entity { public virtual string Name { get; set; } public virtual DateTime CreationTime { get; set; } public Person() { CreationTime = DateTime.Now; } }
Person類定義成一個實體,它有兩個屬性,同時Entity類定義了一個Id屬性,它是這個實體的主鍵。所以所有的實體主鍵名都相同,都是Id。
Id(主鍵)的類型是可改的,預設是int(Int32)。如果你想把Id定義成其它類型,你應該顯式聲明它,如下所示:
public class Person : Entity<long> { public virtual string Name { get; set; } public virtual DateTime CreationTime { get; set; } public Person() { CreationTime = DateTime.Now; } }
同樣,你也可以把它設置成string,Guid或其它類型。
Entity類重寫了equality操作符(==),用它可以非常容易地檢查兩個實體是否相等(它們的Id是否相等),同時也定義了IsTransient()方法檢查實體是否有一個Id。
“聚合在DDD里是一個模式,一個DDD聚合是一個領域對象群,可由單獨的單元創建。例如一個訂單和它的項,這些可以是分離的對象,但把訂單(和它的項一起)看成成是一個單獨的聚合是有用的。“(Martin Fowler 查看完整描述)。
雖然ABP沒有強迫你使用聚合,但你也可能想在你的應用里,創建聚合和聚合根。ABP擴展了Entity,定義了AggregateRoot類,為一個聚合創建聚合根實體。
AggregateRoot定義了DomainEvents集合,通過聚合根對象產生領域事件。這些事件在當前工作單元完成前自動觸發,實質上,任何實體都可以通過實現IGeneratesDomainEvents介面產生領域事件,但通常(最佳實踐)在聚合根里產生領域事件,這就是為什麼把它預設到AggregateRoot里,而不是Entity類里。
在很多應用里,有很多相似的實體屬性(資料庫表的欄位),如表示實體何時創建的CreationTime,ABP提供了一些有用的介面,明確和展現這些通用屬性,這也給實現這些介面的實體,在編寫這些屬性代碼時提供了一種通用的方式。
IHasCreationTime為一個實體的“創建時間”信息採用通用的屬性,在一個實體插入到資料庫前,ABP自動為實現了該介面的實體,設置CreationTime屬性為當前時間。
public interface IHasCreationTime { DateTime CreationTime { get; set; } }
Person類改寫成實現IHasCreationTime介面,如下所示:
public class Person : Entity<long>, IHasCreationTime { public virtual string Name { get; set; } public virtual DateTime CreationTime { get; set; } public Person() { CreationTime = DateTime.Now; } }
ICreationAudited通過添加CreatorUserId擴展了IhasCreationTime:
public interface ICreationAudited : IHasCreationTime { long? CreatorUserId { get; set; } }
當保存一個新實體時,ABP自動把CreatorUserId設置為當前用戶的id。你也可以讓你的類繼承CreationAuditedEntity類實現ICreationAudited。它同時也有一個適用於不同類型Id屬性的泛型版本。
也有一個類似的“修改”介面
public interface IHasModificationTime { DateTime? LastModificationTime { get; set; } } public interface IModificationAudited : IHasModificationTime { long? LastModifierUserId { get; set; } }
當更新一個實體時,ABP也自動設置這些屬性。你只需要為你的類定義它們就可以。
如果你想實現所有審計屬性,你可以直接實現IAudited介面:
public interface IAudited : ICreationAudited, IModificationAudited { }
更快捷的方式是:你可以繼承AuditedEntity類來代替直接實現IAudited。AuditiedEntity類同樣也有一個適用於不同類型Id屬性的泛型版本。
註意:ABP從ABP會話里獲取用戶Id。
軟刪除是一個通用的模式,它把一個實體標記為“已刪除”代替從資料庫直接刪除。例如,你不想把一個User從資料庫硬刪除,因為它可能與其它表有關聯,ISoftDelete介面就是出於這種目的:
public interface ISoftDelete { bool IsDeleted { get; set; } }
ABP實現了開箱即用模式的軟刪除模式。當一個軟刪除實體開始刪除時,ABP檢測它,阻止它被刪除,設置IsDeleted為true,並把實體更新到資料庫。同時,ABP不會從資料庫獲取(select)軟刪除的實體,會自動過濾它們。
如果你使用軟刪除,當軟刪除一個實體時,你可能也會想保存是誰刪除和什麼時候刪除,你可以實現IDeletionAudited介面,如下所示:
public interface IDeletionAudited : ISoftDelete { long? DeleterUserId { get; set; } DateTime? DeletionTime { get; set; } }
更快捷的方式是:你可以從已經實現了所有的FullAuditedEntity類繼承你的實體。
- 註意1:所有審計介面和類都有一個為指向你的User實體的導航屬性而設計的泛型版本(如ICreationAudited<TUser>和FullAuditedEntity<TPrimaryKey,TUser>)。
- 註意2:同時,它們都有一個AggregateRoot版本,如AuditedAggregateRoot。
有些實體需要標記為Active(活躍的)和Passive(消極的),然後你根據實體的活躍/消極狀態採取行動。你可以實現為此目的而生的IPassivable,它定義了IsActive屬性。
如果你的實體想在創建時就是處理活躍狀態,你可以在構造器里設置IsActive為true。
這與軟刪除(IsDeleted)不同,如果一個實體被軟刪除,它就不能再從資料庫里獲取到(ABP預設阻止它),但是是否獲取活躍/消極實體完全取決於你。
當一個實體插入、更新、刪除時,ABP會自動觸發某些事件,因此你可以註冊這些事件執行你需要的任何邏輯。查看事件匯流排文檔的“預定義事件”主題,獲取更多信息。
實質上,Entity類實現了IEntity介面(且Entity<TPrimaryKey>實現了IEntity<TPrimaryKey>)。如果你不想從Entity類繼承,你可以直接實現這些介面,這些介面對於其它實體類也是適用的,但是這不是推薦的方式,除非你有一個好的理由。