在.NET衆多的技術框架中,ABP框架(本系列中指aspnetboilerplate項目)以其獨特的魅力吸引了一羣優秀開發者普遍的使用。前端
在該框架的賦能之下,開發者可根據需求經過官方網站【https://aspnetboilerplate.com/Templates】選擇下載例如Vue/AngluarJS/MVC等不一樣類型的模板項目,輕鬆加入ABP開發者的隊伍中,盡享基於ABP開發帶來的樂趣。
vue
ABP開發框架也提供了豐富的文檔,可以爲開發者帶來許多便捷。目前ABP的文檔網站爲:ios
官方文檔:https://aspnetboilerplate.com/Pages/Documentsweb
文檔庫不可謂不全,加上國內衆多的ABP開發者參與的活躍的技術圈子,使得學習成本只是在第一個項目中比較高,後期將會愈來愈平滑。vuex
固然,目前ABP的框架開發者和社區已經把更多的精力投入到了ABP.VNEXT開發框架,這個新框架以其DDD+微服務+模塊化的理念得到了大量擁躉,使ABP框架的開發優先級已經開始逐漸下降。docker
但這是由於ABP框架的功能已經成熟穩定,且ABP是一種增量式的架構設計,開發者在熟練掌握這種框架後,能夠根據本身的須要進行方便的擴展,使其成爲小項目架構選型中一種不錯的備選方案。typescript
固然,也存在一些弊端。例如因爲ABP被稱爲.NET衆多開發框架中面向領域驅動設計的最佳實踐,而囿於領域驅動設計自己不低的門檻,使得學習的過程變得看起來很是陡峭;數據庫
除此以外,ABP也普遍使用了目前Asp.NET/Asp.NET Core框架的大量比較新的特性,對於很多沒法因爲各類緣由沒法享受.NET技術飛速發展紅利的傳統開發者來講,無形中也提升了技術門檻。axios
在這個系列中,本文計劃分紅三篇來介紹ABP框架,第一篇介紹ABP的基礎概覽,介紹基礎知識,第二篇介紹ABP的模式實踐,第三篇,試圖介紹如何從更傳統的三層甚至是單層+SQL的單層架構,如何遷移到ABP框架。c#
(畢竟。。.NET遺留應用實在是太多了,拯救或不拯救?)
當咱們經過ABP模板項目的官方網站下載一個項目後,咱們所得到的代碼包的結構以下圖所示,其中:
打開vue文件夾以後,該項目的基本目錄以下圖所示。(src文件夾)
定義了與abp+vue腳手架項目的基礎組件和常見類庫,封裝了一系列基本方法。例如權限控制,數據請求,菜單操做,SignalR等基礎組件的用法。
定義了vue項目的路由規則,其中index.ts文件是項目的入口,router.ts文件定義了vue文件的路由規則。
因爲本項目使用了vuex框架,因此咱們能夠來看看對於store文件夾的介紹。
在vuex框架中:
每個 Vuex 應用的核心就是 store(倉庫)。「store」基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。
Vuex 和單純的全局對象有如下兩點不一樣:
Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新。
你不能直接改變 store 中的狀態。改變 store 中的狀態的惟一途徑就是顯式地提交 (commit) mutation。這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。
即vuex框架中,將原來的請求鏈路,抽象化爲狀態的變化,經過維護狀態,使得數據的管理更加便捷,也易於擴展。
定義了登陸、首頁、用戶、角色、租戶的基本頁面,並提供了新增、查看、編輯、刪除的代碼示例。
綜上,該項目是一個結構清晰,邏輯縝密的前端框架,能夠做爲常見管理系統的腳手架。
後端項目是一個遵循了領域驅動設計的分層,同時又符合Robert Martin在《代碼整潔之道》提出的【整潔架構】。
在領域驅動設計的分層設計中,共有四個功能分層,分別是:
表示層(Presentation Layer):爲用戶提供接口,使用應用層實現用戶交互。
應用層(Application Layer):介於用戶層和領域層之間,協調用戶對象,完成對應的任務。
領域層(Domain Layer):包含業務對象和規則,是應用程序的心臟。
基礎設施層(Infrastructure Layer):提供高層級的通用技術功能,主要使用第三方庫完成。
在後文中,基於abp對領域驅動設計的功能分層將進行屢次、詳細敘述,本小節再也不贅述。
整潔架構是由Bob大叔提出的一種架構模型,來源於《整潔架構》這本書,顧名思義,其目的並非爲了介紹這一種優秀的架構自己,而是介紹如何設計一種整潔的架構,使得代碼結構易於維護。
(整潔架構就是這樣一個洋蔥,因此也有人稱它爲「洋蔥」架構)
用一組同心圓來表示軟件的不一樣領域。通常來講,越深刻表明你的軟件層次越高。外圓是戰術是實現機制(mechanisms),內圓的是核心原則(policy)。
Policy means the application logic.
Mechanism means the domain primitives.
使此體系架構可以工做的關鍵是依賴規則。這條規則規定軟件模塊只能向內依賴,而裏面的部分對外面的模塊一無所知,也就是內部不依賴外部,而外部依賴內部。一樣,在外面圈中使用的數據格式不該被內圈中使用,特別是若是這些數據格式是由外面一圈的框架生成的。咱們不但願任何外圓的東西會影響內圈層
實體封裝的是整個企業範圍內的業務核心原則(policy),一個實體能是一個帶有方法的對象,或者是一系列數據結構和函數,只要這個實體可以被不一樣的應用程序使用便可。
若是你沒有編寫企業軟件,只是編寫簡單的應用程序,這些實體就是應用的業務對象,它們封裝着最普通的高級別業務規則,你不能但願這些實體對象被一個頁面的分頁導航功能改變,也不能被安全機制改變,操做實現層面的任何改變不能影響實體層,只有業務需求改變了才能夠改變實體
在這個層的軟件包含只和應用相關的業務規則,它封裝和實現系統的全部用例,這些用例會混合各類來自實體的各類數據流程,而且指導這些實體使用企業規則來完成用例的功能目標。
咱們並不指望改變這層會影響實體層. 咱們也不指望這層被更外部如數據庫 UI或普通框架影響,而這也正是咱們分離出這一層來的緣由所在。
然而,應用層面的操做改變將會影響到這個用例層,若是需求中用例發生改變,這個層的代碼就會隨之發生改變。因此能夠看到,這一層是和應用自己緊密相關的
這一層的軟件基本都是一些適配器,主要用於將用例和實體中的數據轉換爲外部系統如數據庫或Web使用的數據,在這個層次,能夠包含一些GUI的MVC架構,表現視圖 控制器都屬於這個層,模型Model是從控制器傳遞到用例或從用例傳遞到視圖的數據結構。
一般在這個層數據被轉換,從用例和實體使用的數據格式轉換到持久層框架使用的數據,主要是爲了存儲到數據庫中,這個圈層的代碼是一點和數據庫沒有任何關係,若是數據庫是一個SQL數據庫, 這個層限制使用SQL語句以及任何和數據庫打交道的事情。
最外面一圈一般是由一些框架和工具組成,如數據庫Database, Web框架等. 一般你沒必要在這個層沒必要寫太多代碼,而是寫些膠水性質的代碼與內層進行粘結通信。
這個層是細節所在,Web技術是細節,數據庫是細節,咱們將這些實現細節放在外面以避免它們對咱們的業務規則形成影響傷害
在ABP項目中,層次劃分以下。
在領域驅動設計的分層式架構中,應用層做爲應用系統的北向網關,對外提供業務外觀的功能。在Abp模板項目中,Application項目也是編寫主要用例代碼的位置,開發者們在此定義與界面有關的數據行爲,實現面向接口的開發實踐。
應用服務層包含應用服務,數據傳輸單元,工做單元等對象。
爲面向用戶界面層實現業務邏輯代碼。例如須要爲某些界面對象組裝模型,一般會定義ApplicationService,並經過DTO對象,實現與界面表現層的數據交換。
最多見的數據結構爲DTO(數據傳輸對象),這是來源於馬丁弗勒在《企業架構應用模式》中提到的名詞,其主要做用爲:
是一種設計模式之間傳輸數據的軟件應用系統。 數據傳輸目標每每是數據訪問對象從數據庫中檢索數據。
在ABP的設計中,有兩種不一樣類型的DTO,分別是用於新增、修改、刪除的Input DTO,和用於查詢的Output DTO。
工做單元。工做單元與事務相似,封裝了一系列原子級的數據庫操做。
核心層包含領域實體、值對象、聚合根,以及領域上下文實現。
實體有別於傳統意義上你們所理解的與數據庫字段一一匹配的實體模型,在領域驅動設計中,雖然實體一樣可能持久化到數據庫,但實體包含屬性和行爲兩種不一樣的抽象。
例如,若是有一個實體爲User,其中有一個屬性爲Phone,數據爲086-132xxxxxxxx,咱們有時須要判斷該手機號碼的國際代號,可能會添加一個新的斷定 GetNationCode(),能夠經過從Phone字段中取出086來實現,這就是一種通俗意義上的行爲。
值對象無需持久化到數據庫,每每是從其餘實體或聚合中「剝離」出來的與某些聚合具有邏輯相關性或語義相關性的對象,有時值對象甚至只有個別屬性。
例如,上述實體,包含Phone字段,咱們能夠將整個Phone「剝離」爲一個Telephone對象,該對象可包含PhoneNumber和NationCode字段。
public class User { public Telephone Phone{public get;private set;} } public class Telephone { public string PhoneNumber {get;set;} public string NationCode {get;set;} }
聚合是業務的最小工做單元,有時,一個實體就是一個小聚合,而爲聚合對外提供訪問機制的對象,就是聚合根。
在領域驅動設計中,識別聚合也是一件很是重要的工做,有一組系統的方法論能夠爲咱們提供參考。
固然,事實上識別領域對象,包括且不限定於識別聚合、值對象、實體識別該對象的行爲或(方法)自己是一件須要經驗完成的工做,有時須要UML建模方法的普遍參與。
有時,咱們會習慣於經過屬性賦值完成梭代碼的過程,從而形成領域行爲流失在業務邏輯層的問題,那麼或許能夠採起這樣的方法:
一、對象的建立,使用構造函數賦值,或工廠方法建立。
二、將全部對於屬性的訪問級別都設置爲
public string Phone{public get;private set;}
而後再經過一個綁定手機號碼的方法,來給這個對象設置手機號碼。
public string BindPhone(string phone) { }
將全部一切涉及到對Phone的操做,都只能經過規定的方法來賦值,這樣能夠實現咱們開發過程當中,無心識的經過屬性賦值,可能致使的「領域行爲」丟失的現象發生。
這種方式可使得對對象某些屬性的操做,只能經過惟一的入口完成,符合單一職責原則的合理運用,若是要擴展方法,可使用開閉原則來解決。
可是,採用這種方式,得儘可能避免出現:SetPhone(string phone) 這樣的方法出現,畢竟這樣的方法,其實和直接的屬性賦值,沒有任何區別。
倉儲封裝了一系列對象數據庫操做的方法,完成對象從數據庫到對象的轉換過程。在領域驅動設計中,一個倉儲每每會負責一個聚合對象從數據庫到建立的全過程。
領域服務就是「實幹家」,那些不適合在領域對象中出現,又不屬於對象數據庫操做的方法,又與領域對象息息相關的方法,均可以放到領域服務中實現。
規範模式是一種特殊的軟件設計模式,經過使用布爾邏輯將業務規則連接在一塊兒,能夠從新組合業務規則。
實際上,它主要用於爲實體或其餘業務對象定義可重用的過濾器。
EntityFrameworkCore負責定義數據庫上下文和對EFCore操做的一系列規則、例如種子數據的初始化等。
Web.Core:定義了應用程序的外觀和接口。雖然從表面上看,Web.Core定義了做爲Web訪問入口的控制器方法和登陸驗證的邏輯,看起來像是用戶表現層的東西,可是仔細想一想,這些東西,未嘗不是一種基礎設施?
Web.Host:定義WEB應用程序的入口。
本文簡述了ABP框架的先後端項目的分層結構,經過了解這些結構,將有助於咱們在後續的實戰中更快入手,爲應用開發插上翅膀。