如何使用ABP進行軟體開發之基礎概覽

溪源More發表於2020-06-28

ABP框架簡述

1)簡介

在.NET眾多的技術框架中,ABP框架(本系列中指aspnetboilerplate專案)以其獨特的魅力吸引了一群優秀開發者廣泛的使用。

在該框架的賦能之下,開發者可根據需求通過官方網站【https://aspnetboilerplate.com/Templates】選擇下載例如Vue/AngluarJS/MVC等不同型別的模板專案,輕鬆加入ABP開發者的隊伍中,盡享基於ABP開發帶來的樂趣。

ABP開發框架也提供了豐富的文件,能夠為開發者帶來許多便捷。目前ABP的文件網站為:

官方文件:https://aspnetboilerplate.com/Pages/Documents

文件庫不可謂不全,加上國內眾多的ABP開發者參與的活躍的技術圈子,使得學習成本只是在第一個專案中比較高,後期將會越來越平滑。

2)現狀

當然,目前ABP的框架開發者和社群已經把更多的精力投入到了ABP.VNEXT開發框架,這個新框架以其DDD+微服務+模組化的理念獲得了大量擁躉,使ABP框架的開發優先順序已經開始逐漸降低。

但這是因為ABP框架的功能已經成熟穩定,且ABP是一種增量式的架構設計,開發者在熟練掌握這種框架後,可以根據自己的需要進行方便的擴充套件,使其成為小專案架構選型中一種不錯的備選方案。

當然,也存在一些弊端。例如由於ABP被稱為.NET眾多開發框架中面向領域驅動設計的最佳實踐,而囿於領域驅動設計本身不低的門檻,使得學習的過程變得看起來非常陡峭;

除此之外,ABP也廣泛使用了目前Asp.NET/Asp.NET Core框架的大量比較新的特性,對於不少無法由於各種原因無法享受.NET技術飛速發展紅利的傳統開發者來說,無形中也提高了技術門檻。

3)綜述

在這個系列中,本文計劃分成三篇來介紹ABP框架,第一篇介紹ABP的基礎概覽,介紹基礎知識,第二篇介紹ABP的模式實踐,第三篇,試圖介紹如何從更傳統的三層甚至是單層+SQL的單層架構,如何遷移到ABP框架。

(畢竟。。.NET遺留應用實在是太多了,拯救或不拯救?)

程式碼結構結構

基本資料夾簡述

當我們通過ABP模板專案的官方網站下載一個專案後,我們所獲得的程式碼包的結構如下圖所示,其中:

  • vue為使用iview框架構建的管理系統基本模板,該腳手架使用了yarn作為包管理器,並整合了vuex/axios等常用框架,並提供了使用者,租戶,許可權三個基本功能的示例程式碼,開發者只需發揮聰明才智就能快速的通過該框架入手前端專案。
  • (當然,該專案廣泛使用了typescript+物件導向的設計,似乎前端開發者。。普遍不擅長物件導向開發?)
  • aspnet-core則是一個完整的asp.netcore專案的快速開發腳手架。該腳手架整合了docker打包於一體,幷包含基本的單元測試示例,使用了identity作為許可權控制單元,使用swagger作為介面文件管理工具,整合了efcore、jwt等常用元件,對於開發者來說,基本上算是開箱即用了。

前端vue專案

開啟vue資料夾之後,該專案的基本目錄如下圖所示。(src資料夾)

lib資料夾

定義了與abp+vue腳手架專案的基礎元件和常見類庫,封裝了一系列基本方法。例如許可權控制,資料請求,選單操作,SignalR等基礎元件的用法。

router資料夾

定義了vue專案的路由規則,其中index.ts檔案是專案的入口,router.ts檔案定義了vue檔案的路由規則。

store資料夾

由於本專案使用了vuex框架,所以我們可以來看看對於store資料夾的介紹。

在vuex框架中:

每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。
Vuex 和單純的全域性物件有以下兩點不同:
Vuex 的狀態儲存是響應式的。當 Vue 元件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的元件也會相應地得到高效更新。
你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用。

即vuex框架中,將原來的請求鏈路,抽象化為狀態的變化,通過維護狀態,使得資料的管理更加便捷,也易於擴充套件。

views資料夾

定義了登入、首頁、使用者、角色、租戶的基本頁面,並提供了新增、檢視、編輯、刪除的程式碼示例。

綜上,該專案是一個結構清晰,邏輯縝密的前端框架,可以作為常見管理系統的腳手架。

後端專案

簡介

後端專案是一個遵循了領域驅動設計的分層,同時又符合Robert Martin在《程式碼整潔之道》提出的【整潔架構】。

領域驅動設計簡介

在領域驅動設計的分層設計中,共有四個功能分層,分別是:

表示層(Presentation Layer):為使用者提供介面,使用應用層實現使用者互動。

應用層(Application Layer):介於使用者層和領域層之間,協呼叫戶物件,完成對應的任務。

領域層(Domain Layer):包含業務物件和規則,是應用程式的心臟。

基礎設施層(Infrastructure Layer):提供高層級的通用技術功能,主要使用第三方庫完成。

在後文中,基於abp對領域驅動設計的功能分層將進行多次、詳細敘述,本小節不再贅述。

整潔架構簡介

整潔架構是由Bob大叔提出的一種架構模型,來源於《整潔架構》這本書,顧名思義,其目的並不是為了介紹這一種優秀的架構本身,而是介紹如何設計一種整潔的架構,使得程式碼結構易於維護。

(整潔架構就是這樣一個洋蔥,所以也有人稱它為“洋蔥”架構)

  1. 依賴規則(Dependency Rule)

用一組同心圓來表示軟體的不同領域。一般來說,越深入代表你的軟體層次越高。外圓是戰術是實現機制(mechanisms),內圓的是核心原則(policy)。

Policy means the application logic.

Mechanism means the domain primitives.

使此體系架構能夠工作的關鍵是依賴規則。這條規則規定軟體模組只能向內依賴,而裡面的部分對外面的模組一無所知,也就是內部不依賴外部,而外部依賴內部。同樣,在外面圈中使用的資料格式不應被內圈中使用,特別是如果這些資料格式是由外面一圈的框架生成的。我們不希望任何外圓的東西會影響內圈層

  1. 實體 (Entities)

實體封裝的是整個企業範圍內的業務核心原則(policy),一個實體能是一個帶有方法的物件,或者是一系列資料結構和函式,只要這個實體能夠被不同的應用程式使用即可。

如果你沒有編寫企業軟體,只是編寫簡單的應用程式,這些實體就是應用的業務物件,它們封裝著最普通的高階別業務規則,你不能希望這些實體物件被一個頁面的分頁導航功能改變,也不能被安全機制改變,操作實現層面的任何改變不能影響實體層,只有業務需求改變了才可以改變實體

  1. 用例 (Use case)

在這個層的軟體包含只和應用相關的業務規則,它封裝和實現系統的所有用例,這些用例會混合各種來自實體的各種資料流程,並且指導這些實體使用企業規則來完成用例的功能目標。

我們並不期望改變這層會影響實體層. 我們也不期望這層被更外部如資料庫 UI或普通框架影響,而這也正是我們分離出這一層來的原因所在。

然而,應用層面的操作改變將會影響到這個用例層,如果需求中用例發生改變,這個層的程式碼就會隨之發生改變。所以可以看到,這一層是和應用本身緊密相關的

  1. 介面介面卡 (Interface Adapters)

這一層的軟體基本都是一些介面卡,主要用於將用例和實體中的資料轉換為外部系統如資料庫或Web使用的資料,在這個層次,可以包含一些GUI的MVC架構,表現檢視 控制器都屬於這個層,模型Model是從控制器傳遞到用例或從用例傳遞到檢視的資料結構。

通常在這個層資料被轉換,從用例和實體使用的資料格式轉換到持久層框架使用的資料,主要是為了儲存到資料庫中,這個圈層的程式碼是一點和資料庫沒有任何關係,如果資料庫是一個SQL資料庫, 這個層限制使用SQL語句以及任何和資料庫打交道的事情。

  1. 框架和驅動器

最外面一圈通常是由一些框架和工具組成,如資料庫Database, Web框架等. 通常你不必在這個層不必寫太多程式碼,而是寫些膠水性質的程式碼與內層進行粘結通訊。

這個層是細節所在,Web技術是細節,資料庫是細節,我們將這些實現細節放在外面以免它們對我們的業務規則造成影響傷害

ABP的分層實現

在ABP專案中,層次劃分如下。

1. 應用層(Application專案)

在領域驅動設計的分層式架構中,應用層作為應用系統的北向閘道器,對外提供業務外觀的功能。在Abp模板專案中,Application專案也是編寫主要用例程式碼的位置,開發者們在此定義與介面有關的資料行為,實現面向介面的開發實踐。

應用服務層包含應用服務,資料傳輸單元,工作單元等物件。

  • Application Service

為面向使用者介面層實現業務邏輯程式碼。例如需要為某些介面物件組裝模型,通常會定義ApplicationService,並通過DTO物件,實現與介面表現層的資料交換。

  • Data Transfer Object (DTO)

最常見的資料結構為DTO(資料傳輸物件),這是來源於馬丁弗勒在《企業架構應用模式》中提到的名詞,其主要作用為:

是一種設計模式之間傳輸資料的軟體應用系統。 資料傳輸目標往往是資料訪問物件從資料庫中檢索資料。

在ABP的設計中,有兩種不同型別的DTO,分別是用於新增、修改、刪除的Input DTO,和用於查詢的Output DTO。

  • Unit of Work:

工作單元。工作單元與事務類似,封裝了一系列原子級的資料庫操作。

2. 核心層(Core專案)

核心層包含領域實體、值物件、聚合根,以及領域上下文實現。

  • Entity(實體):

實體有別於傳統意義上大家所理解的與資料庫欄位一一匹配的實體模型,在領域驅動設計中,雖然實體同樣可能持久化到資料庫,但實體包含屬性和行為兩種不同的抽象。

例如,如果有一個實體為User,其中有一個屬性為Phone,資料為086-132xxxxxxxx,我們有時需要判斷該手機號碼的國際代號,可能會新增一個新的判定 GetNationCode(),可以通過從Phone欄位中取出086來實現,這就是一種通俗意義上的行為。

  • Value Object(值物件):

值物件無需持久化到資料庫,往往是從其他實體或聚合中“剝離”出來的與某些聚合具備邏輯相關性或語義相關性的物件,有時值物件甚至只有個別屬性。

例如,上述實體,包含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;}
}
  • Aggregate & Aggregate Root(聚合,聚合根):

聚合是業務的最小工作單元,有時,一個實體就是一個小聚合,而為聚合對外提供訪問機制的物件,就是聚合根。

在領域驅動設計中,識別聚合也是一件非常重要的工作,有一組系統的方法論可以為我們提供參考。

當然,事實上識別領域物件,包括且不限定於識別聚合、值物件、實體識別該物件的行為或(方法)本身是一件需要經驗完成的工作,有時需要UML建模方法的廣泛參與。

有時,我們會習慣於通過屬性賦值完成梭程式碼的過程,從而造成領域行為流失在業務邏輯層的問題,那麼或許可以採取這樣的方法:

1、物件的建立,使用建構函式賦值,或工廠方法建立。

2、將所有對於屬性的訪問級別都設定為

public string Phone{public get;private set;}

然後再通過一個繫結手機號碼的方法,來給這個物件設定手機號碼。

public string BindPhone(string phone)
{
}

將所有一切涉及到對Phone的操作,都只能通過規定的方法來賦值,這樣可以實現我們開發過程中,無意識的通過屬性賦值,可能導致的“領域行為”丟失的現象發生。
這種方式可以使得對物件某些屬性的操作,只能通過唯一的入口完成,符合單一職責原則的合理運用,如果要擴充套件方法,可以使用開閉原則來解決。

但是,採用這種方式,得儘量避免出現:SetPhone(string phone) 這樣的方法出現,畢竟這樣的方法,其實和直接的屬性賦值,沒有任何區別。

  • Repository(倉儲)

倉儲封裝了一系列物件資料庫操作的方法,完成物件從資料庫到物件的轉換過程。在領域驅動設計中,一個倉儲往往會負責一個聚合物件從資料庫到建立的全過程。

  • Domain Service(領域服務)

領域服務就是“實幹家”,那些不適合在領域物件中出現,又不屬於物件資料庫操作的方法,又與領域物件息息相關的方法,都可以放到領域服務中實現。

  • Specification(規格定義)

規範模式是一種特殊的軟體設計模式,通過使用布林邏輯將業務規則連結在一起,可以重新組合業務規則。

實際上,它主要用於為實體或其他業務物件定義可重用的過濾器。

3. 其他基礎設施(EntityFrameworkCore,Web.Core,Web.Host專案)

EntityFrameworkCore負責定義資料庫上下文和對EFCore操作的一系列規則、例如種子資料的初始化等。

Web.Core:定義了應用程式的外觀和介面。雖然從表面上看,Web.Core定義了作為Web訪問入口的控制器方法和登入驗證的邏輯,看起來像是使用者表現層的東西,但是仔細想想,這些東西,何嘗不是一種基礎設施?

Web.Host:定義WEB應用程式的入口。

總結

本文簡述了ABP框架的前後端專案的分層結構,通過了解這些結構,將有助於我們在後續的實戰中更快入手,為應用開發插上翅膀。

相關文章