概述
在DDD中,業務邏輯主要分布在領域層和應用層兩層,他們包含不同的業務邏輯。這一篇,我們先對領域層做詳細的講解分析。
領域層實現了領域或系統的,與用戶界面上的用戶交互(用例)無關的核心業務邏輯。
總覽
領域層主要包含以下組件:
實體(Entity):實體是一個具有自己的屬性(狀態、數據)和實現在這些屬性上執行的業務邏輯的方法的對象。實體由其唯一標識符(Id)表示。具有不同id的兩個實體對象被視為不同的實體。
聚合和聚合根(Aggregate&Aggregate Root):聚合是通過集合根對象綁定在一起的對象集(實體和值對象)。聚合根是具有一些附加職責的實體的特定類型。
值對象(Value Object):值對象是另一種由其屬性而不是唯一的Id來標識的域對象。這意味著兩個具有相同屬性的值對象將被視為相同的對象。值對象通常被實現為不可變對象,而且大多數值對象都比實體要簡單得多。
領域服務(Domain Service):領域服務是一個實現域的核心業務規則的無狀態服務。它依賴于多個聚合(實體)類型或某些外部服務,用于領域邏輯的實現。
倉儲(Repository)接口:倉儲是一個類似于集合的接口,它被領域層和應用層用于訪問數據持久化系統(數據庫)。它對業務代碼隱藏了DBMS的復雜性。領域層包含倉儲的接口聲明。
規約(Specification):規約用于為實體和其他業務對象定義已命名的、可重用的且可組合的過濾器。
領域事件(Domain Event):領域事件是在領域特定事件發生時以松散耦合的形式通知其他服務的一種方法。
在ABP框架下,領域層包含以下兩個項目:
Domain是包含所有組件(實體、聚合根、值對象、領域服務、規約、存儲庫接口等)的基本領域層。?
Domain.Shared是一個輕量的項目,它包含了屬于領域層的一些類型,但與所有其他層共享。例如,它可能包含一些與域對象相關的常量和枚舉,但需要被其他層重用。
實現細節
1
實體
在ABP中,所有的實體類均需實現ABP框架中定義的接口?IEntity?。ABP中將實體分為兩種用法:有默認主鍵(Id)的實體和沒有默認主鍵的實體。
有默認主鍵的實體在?IEntity?接口基礎上封裝了?IEntity<TKey>?接口,其中,定義了TKey類型的主鍵,名稱為Id。其默認實現類繼承自?Entity<TKey>?。我們使用時,需以泛型方式定義主鍵的類型。
無默認主鍵的實體類直接實現IEntity接口,其默認實現類繼承自?Entity?,在?Entity?中定義了虛方法?object[] GetKeys()?。該方法用于設定一個或多個屬性為主鍵。使用無默認主鍵的實體時,我們必須實現GetKeys方法指定該實體類對應數據庫表的主鍵。
在ABP中,對于實體定義了默認增刪改操作的審計屬性,通過?ICreationAuditedObject?,?IModificationAuditedObject?和?IDeletionAuditedObject?三個接口進行約束,分別包含對添加(添加人,添加時間),修改(修改人、修改時間),刪除(刪除人、刪除時間、是否被軟刪除)操作記錄的審計屬性。ABP為ICreationAuditedObject接口創建了默認實現基類?CreationAuditedEntity?,同時還創建了包含以上三個接口所有實現的基類?FullAuditedEntity?,?CreationAuditedEntity?和?FullAuditedEntity?均包含有默認主鍵和無默認主鍵兩種用法,以是否定義泛型來區分。
ABP中定義了?ISoftDelete?接口,其中包含?IsDeleted?屬性,表示該條數據是否被軟刪除。當實體實現ISoftDelete接口時,ABP的倉儲會自動將刪除方法轉為軟刪除,即只將IsDeleted屬性設置為true,使其部不被查詢到,但并不在數據庫中對數據進行物理刪除。
2
聚合根
在ABP中聚合根需實現I?AggregateRoot?,其默認基類為?AggregateRoot?,同時?AggregateRoot?也實現了?IEntity?接口。?IAggregateRoot?本身未包含任何屬性或者方法的聲明,其主鍵聲明繼承自IEntity接口。
和實體類相同,聚合根也定義了有默認主鍵和無默認主鍵兩種用法,通過是否傳入泛型來進行區分。
聚合根也可以實現?ICreationAuditedObject?, ?IModificationAuditedObject?和?IDeletionAuditedObject?對增刪改操作進行審計記錄,其中實現接口?ICreationAuditedObject?的基類為?CreationAuditedAggregateRoot?,實現所有審計接口的基類為?FullAuditedEntity?。
3
值對象
值對象的定義相對來說非常簡單,在ABP中,定義了值對象的基類 ?ValueObject??,定義值對象必須繼承ValueObject??ValueObject?并實現ValueObject中定義的抽象方法?GetAtomicValues()?。
GetAtomicValues?方法用于檢查兩個值對象是否相等,其用法為通過?yield?return?返回一組屬性,表示如果兩個值對象這些屬性值都相等,則這兩個為相等的值對象。示例代碼如下:
protected override IEnumerable<object> GetAtomicValues() { ? ?yield return Street; ? ?yield return CityId; ? ?yield return Number; }
4
領域服務
領域服務的接口和實現均包含在領域層Domain項目中,其中領域服務接口聲明繼承自ABP提供的?IDomainService?接口,領域服務實現類繼承自ABP提供的?DomainService?類。
在?IDomainService?接口中未聲明任何屬性和方法,我們如果需要用到領域服務,可完全依據自己的需求定義其中的內容。
在ABP框架中,領域服務通過自動依賴注入的方式以瞬態生命周期被注冊到IOC容器中,即每次使用都會生成一個新的實例。我們只需要保證其所在類庫中含有繼承自?AbpModule?的模塊定義類,即可實現自動依賴注入。
5
倉儲接口
在領域層Domain項目中,只會聲明倉儲的接口,其實現需要在基礎設施層的EntityFrameworkCore項目中編寫。
ABP框架已經為我們提供了默認的倉儲接口和實現,在大多數情況下,我們不需要編寫任何倉儲接口或實現類的代碼,只需要在使用時注入?IRepository<TEntity, TKey>?,其中的兩個泛型分別為實體/聚合根的類型和主鍵數據類型。
如果我們需要自定義倉儲,則在Domain層定義其接口聲明,倉儲接口聲明需要繼承自?IRepository?接口。
6
規約
ABP提供了規約的接口?ISpecification<T>?和實現類基類?Specification<T>?,其中泛型為需要約束的實體或聚合根的類型。
編寫規約時必須實現?ISpecification?接口中定義的?ToExpression?方法,該方法用來聲明規約中數據約束的表達式。
同時,?ISpecification?接口中還定義了?IsSatisfiedBy?方法,用于判斷一個對象是否滿足此規約,在?Specification?類中已提供了此方法的默認實現。
7
領域事件
ABP提供了本地事件總線和分布式事件總線兩種方案,其中本地事件總線常用于我們DDD中傳統意義的領域事件實現,而分布式事件總線常用于分布式或微服務系統的事件處理。
ABP本地領域事件使用主要分為訂閱和發布兩個部分:
發布和訂閱的事件或對應關系以其傳輸的數據類型為區分。對于每種事件,我們都需要單獨定義一個事件類型的簡單類(ABP未提供統一基類,直接繼承自Object即可)用于存放傳輸的數據,即使不需要傳輸數據,我們也要定義一個空類來區分事件類型。該類型命名通常以Event結尾。
在DomainService、ApplicationService、Controller等可以使用依賴注入的組件中,我們可以注入?ILocalEventBus?接口并調用?PublishAsync?方法來發布事件,在Entity和Aggregate Root中無法使用依賴注入,ABP官方給他們提供了一個基礎方法?AddLocalEvent?用于發布事件。
在訂閱事件時我們需要定義一個類,實現?ILocalEventHandler<T>??其中的泛型為前面所說的事件類型定義,并實現接口中的?HandleEventAsync?方法用于處理事件。
END
關注我獲得
更多精彩