ABP 框架實(shí)戰(zhàn)系列(二)- 領(lǐng)域?qū)咏榻B篇
EF Core 全稱是Entity Framework Core,可使用 EF Core 開發(fā)面向 .NET Core 的應(yīng)用,EF Core 同時(shí)支持在 Visual StudioVisual Studio for Mac 或 Visual Studio Code 等環(huán)境下開發(fā)。
若有朋友對EF Core不是很熟悉,可以通過篇文章了解一下EF Core 快速入門教程,在此就不重復(fù)介紹了,該公眾號(hào)推薦關(guān)注,知識(shí)點(diǎn)滿滿。
EF Core 之所以在ABP模板中特別抽出來,并不是因?yàn)檫@里的EF Core 有何特別之處,而是這里的EF Core 對領(lǐng)域?qū)ο筘?fù)責(zé)。
ABP框架架構(gòu)介紹中有提到 ABP 不僅僅是一個(gè)架構(gòu),它還是提供了一個(gè)最佳實(shí)踐的基于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的體系結(jié)構(gòu)模型。
如下圖所示,ABP在應(yīng)用層和持久化層中間,有一個(gè)領(lǐng)域?qū)?
領(lǐng)域?qū)痈攀?/strong>
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD):DDD原本定義為一種軟件開發(fā)的解決方案,始于統(tǒng)一語言,經(jīng)由綁定上下文,最終得到一個(gè)以業(yè)務(wù)模型對象為中心的分層架構(gòu)。遵循DDD的原則,常見的開發(fā)步驟:了解需求、把需求轉(zhuǎn)化為規(guī)范、實(shí)際編碼以及測試。DDD的目的是應(yīng)付業(yè)務(wù)領(lǐng)域的核心復(fù)雜行。但是,使用DDD需要建立在對業(yè)務(wù)領(lǐng)域的精準(zhǔn)把握之上。正確得使用DDD會(huì)是軟件的開發(fā)實(shí)施變得非常簡單
領(lǐng)域?qū)拥慕Y(jié)構(gòu)大致如下
領(lǐng)域?qū)拥膬?nèi)部
-
領(lǐng)域模型
模塊
-
值對象
DDD領(lǐng)域模型包含實(shí)體和值對象。值對象完全通過它的特性來定義。值對象的特性在實(shí)例創(chuàng)建之后就不會(huì)改變了。如果要改同時(shí)變,值對象會(huì)變成另一個(gè)值對象的實(shí)例
-
實(shí)體Entity Class
實(shí)體是從實(shí)體類派生的
所有對象都有特性,但不是所有的對象都能完全通過它們的特性集合來標(biāo)識(shí)。這些對象就是實(shí)體。換句話說,如果對象需要一個(gè)ID特性在整個(gè)生命周期的上下文里唯一跟蹤它,這個(gè)對象就有一個(gè)身份標(biāo)識(shí),且被看成實(shí)體。值對象只是聚合在一起的數(shù)據(jù),實(shí)體通常由數(shù)據(jù)和行為構(gòu)成。行為是區(qū)別對待領(lǐng)域邏輯和應(yīng)用程序邏輯的關(guān)鍵。領(lǐng)域邏輯在領(lǐng)域?qū)又?,用例的?shí)現(xiàn)則在應(yīng)用程序里public class Person : Entity { public virtual string Name { get; set; } public virtual DateTime CreationTime { get; set; } public Person() { CreationTime = DateTime.Now; } }人員類定義為一個(gè)實(shí)體。它有兩個(gè)屬性。此外,實(shí)體類定義id屬性。它是實(shí)體的主鍵。因此,所有實(shí)體的主鍵名稱相同,它是id。
id(主鍵)的類型可以更改。這是int(Int32)默認(rèn)。如果要將另一類型定義為id,則應(yīng)顯式聲明如下所示:
public class Person : Entity<long> { public virtual string Name { get; set; } public virtual DateTime CreationTime { get; set; } public Person() { CreationTime = DateTime.Now; } }-
實(shí)體持久化
領(lǐng)域模型必須持久化,但是,它不關(guān)心持久化。領(lǐng)域模型的實(shí)現(xiàn)里沒有涉及加載和保存的操作。但是持久化操作對于一個(gè)應(yīng)用程序時(shí)必不可少的部分。這時(shí),由倉儲(chǔ)負(fù)責(zé)這些工作。倉儲(chǔ)通常都是在領(lǐng)域模型之外使用,比如應(yīng)用程序?qū)?、領(lǐng)域服務(wù)。但是,倉儲(chǔ)的契約在領(lǐng)域?qū)?,它的?shí)現(xiàn)在基礎(chǔ)設(shè)施層理。
聚合
當(dāng)開發(fā)者根據(jù)需求里的用例為綁定上下文構(gòu)建領(lǐng)域模型,你會(huì)發(fā)現(xiàn)一些單個(gè)實(shí)體總是互相引用。邏輯上相關(guān)的對象被單獨(dú)對待而不是組合起來且被當(dāng)作一個(gè)整體對待很容易導(dǎo)致數(shù)據(jù)泥團(tuán)代碼。這時(shí)可以通過聚合對模型里的實(shí)體進(jìn)行分組和隔離。通常的做法是先把領(lǐng)域模型分解成聚合,然后在聚合里標(biāo)識(shí)出領(lǐng)域?qū)嶓w。
-
優(yōu)點(diǎn)
- 業(yè)務(wù)邏輯變得更加簡單
- 防止緊耦合模型
-
關(guān)系
聚合的根類對調(diào)用方隱藏了相關(guān)的類,且要求調(diào)用方在進(jìn)行任何交互時(shí)引用它們。換句話說,一個(gè)實(shí)體只允許引用同一個(gè)聚合的實(shí)體或者另一個(gè)實(shí)體的根。當(dāng)一個(gè)聚合需要引用多個(gè)另一個(gè)聚合中非跟類的實(shí)體,該聚合應(yīng)該引用另一個(gè)聚合的根實(shí)體。
-
創(chuàng)建
聚合根對象是組成這個(gè)聚合的對象群的根。聚合根在整個(gè)領(lǐng)域模型都可見,且可以直接引用。聚合里的實(shí)體任有它們的身份標(biāo)識(shí)和生命周期,但是它們不能從聚合之前直接引用。聚合根也有相應(yīng)的責(zé)任:
- 聚合根保證聚合內(nèi)的對象總是按照應(yīng)用程序iyewu規(guī)則有效狀態(tài)
- 聚合根負(fù)責(zé)持久化所有被封裝的對象
- 聚合根負(fù)責(zé)級(jí)聯(lián)更新以及刪除聚合里的實(shí)體
- 查詢操作只能獲取聚合根。
-
領(lǐng)域服務(wù)
領(lǐng)域服務(wù)類的方法實(shí)現(xiàn)的領(lǐng)域邏輯不屬于特定聚合,且可能跨越多個(gè)實(shí)體。為了實(shí)現(xiàn)業(yè)務(wù)操作,領(lǐng)域服務(wù)協(xié)調(diào)聚合和倉儲(chǔ)的活動(dòng)。在某些時(shí)候,領(lǐng)域服務(wù)可能使用基礎(chǔ)設(shè)施服務(wù),比如發(fā)送電子郵件或者短信
- 服務(wù)即契約
- 跨聚合行為
- 倉儲(chǔ)
-
領(lǐng)域事件
領(lǐng)域事件用于在某些領(lǐng)域事件發(fā)生時(shí)觸發(fā)一個(gè)事件。這樣可以避免在同一個(gè)地方放置所有處理代碼。
- 開發(fā)人員可以在不觸碰產(chǎn)生事件的代碼的情況下動(dòng)態(tài)定義一組處理器
- 可以在多個(gè)地方觸發(fā)相同的事件