一.概述
本章Web架構分層指南,參考了“Microsoft應用程式體系結構指南”(該書是在2009年出版的,當時出版是為了幫助開發人員和架構師更快速,更低風險地使用Microsoft平台和.NET Framework設計和構建有效,高品質的應用程式)。雖然已過去十年了,技術架構已更新(如流行的DDD/CQRS模式,微服務,容器),但web分層思想還是一樣可取,下面是一個“傳統N分層設計”架構圖,該架構在2010年左右是最流行的,包括了表現層presentation,服務層services,業務層business,數據訪問層data,橫切關注點cross,如下所示:
對比傳統多層或三層.net web架構,下圖是當前流行的.net web微服務架構,在web程式分層之上還包含了容器,web api網關,各服務對應的數據存儲(sqlserver,redis,mongoDB),web程式有web api並結合應用了DDDCQRS分層模式,以及系統各種中間件。
下圖是一個訂單微服務站點,包含了簡化的cqrs分層,藍色長方格是表示cqrs分層的職責,包括了查詢 Queries viewModels和命令Command Domain-Model以及上層的應用服務層Application,如下所示
1.1 邏輯分層設計架構類型
(1) 最傳統的分層是經典三層設計,包括表現層,業務層,數據層.
(2) 基於服務的解決方案SOA,公開應用程式業務功能的服務層,服務層在業務層之上。
(3) 基於領域驅動設計的DDDCQRS分層模式
(4) 微服務架構。
這4種web分層架構是不斷的演化改變 ,每一種分層架構並不是獨立的思想,它包含了演化之前的架構分層思想。從以前三層架構到現在的微服務架構,是適應每個時代互聯網業務實現的需求。
功能 | SOA | 微服務 |
組件大小 | 大塊業務邏輯 | 單獨任務或小塊業務邏輯 |
耦合 | 通常松耦合 | 總是松耦合 |
公司架構 | 任何類型 | 小型、專註於功能交叉團隊 |
管理 | 著重中央管理 | 著重分散管理 |
目標 | 確保應用能夠交互操作 | 執行新功能、快速拓展開發團隊 |
二.Web分層設計步驟
1.分層策略
(1)分層粒度是確定分層策略的關鍵第一步.
(2)在邏輯分層中, 多層是在同一進程中運行,這可以利用更高性能的通訊機制。例如通過組件介面直接調用。必須小心保持層之間的封裝和鬆散耦合。
(3)在物理分層中,確定合適的通訊機制,該機制考慮到通訊延遲並保持之間的鬆散耦合。
(4)多層中,考慮它們之間如何相互影響,將確保性能和靈活性之間的良好平衡。
2.確定需要層
最常用的方法是將表現層,服務層,業務層和數據訪問層功能分離到單獨的層中。某些應用程式還引入了各種組件像快取、日誌、消息隊列等。
3.決定如何分發各層和組件
對於web體系架構,一般都是在一個物理層,只有在必要時,才應在不同的物理層上分布層和組件。這是實現分散式部署的常見原因包括安全策略,物理約束,共享業務邏輯和可伸縮性。
4.確定是否需要摺疊層
一般規則是您應始終將功能分組到層中。在某些情況下,一個層可以充當代理或傳遞層,提供封裝和鬆散耦合,而不提供大量功能。但是,通過分離該功能,您可以稍後對其進行擴展,而對設計中的其他層幾乎沒有影響,如:應用服務層。
5.確定層之間引用的規則
在分層策略時,您必須定義層如何相互交互的規則(交互是指:各層引用的關係)。指定交互規則的主要原因是最小化依賴性並消除循環引用。因此應該遵循以下方法之一:
(1)自上而下的交互
較高級別的層可以與下面的層交互,但是較低級別的層不應該與上面的層交互。此規則將幫助您避免層之間的循環依賴關係,以及要降低層之間的依賴性。
(2)嚴格的互動
每個層必須僅與下面的層進行交互。此規則將強制嚴格分離關注點,其中每個層僅知道下面的層。此規則的好處是對層介面的修改只會影響上面的層。如果您正在設計一個將隨著時間的推移進行修改以引入新功能並且您希望最大限度地減少這些更改的影響的應用程式,或者您正在設計可能分布在不同物理層上的應用程式,請考慮使用此方法。
(3)鬆散的互動
較高級別的層可以繞過層直接與較低級別的層交互。這可以提高性能,但也會增加依賴性。換句話說,對較低級別層的修改可以影響上面的多個層。
下圖是一個示例:該web架構示例是使用了 cqrs 模式,涉及到了事件源es, 事件源實現本因該分離到命令域和查詢域, 而項目中應用服務層直接引用了底層數據訪問層Dapper(繞過了領域層), 這樣底層Dapper介面方法的修改或換成EF將影響頂層應用服務層,這屬於第三種”鬆散的互動”。 應該推薦使用第一種自上而下的交互。
6.確定跨領域問題
定義層後,必須標識跨越層的功能。此功能通常被描述為橫切關注點,包括日誌記錄,快取,驗證,身份驗證和異常管理。確定應用程式中的每個橫切關注點非常重要,並設計單獨的組件以儘可能地管理這些問題。此方法可幫助您實現更好的可重用性和可維護性。
如下圖所示:NLog與Redis是具體實現組件,實現了Common層中的日誌和快取介面,Common層就是橫切組件,是跨層可重用的。像Ioc也是橫切組件。 下圖層的名稱沒有標識跨越層的功能,如果是GFNetCore.Infra.CrossCutting.IoC 這樣命名會更好。
7.定義層之間的介面
為層定義介面時,主要目標是強制層之間的鬆散耦合。這意味著層不應暴露另一層可能依賴的內部細節。相反,層的介面應設計為通過提供隱藏層內組件細節的公共介面來最小化依賴性。這種隱藏稱為抽象,有許多不同的方法來實現它。以下設計方法可用於定義層的介面:
(1)抽象介面
通過定義抽象基類或介面類來實現,該類充當具體類的類型定義。該類型定義了一個公共介面,該層的所有使用者都使用該介面與該層進行交互。這是一種面向介面編程,例如:表現層調用應用服務層的介面。表現層在CustomerController控制器中如下所示(通過依賴注入後,構造方法來實例):
//表現層調用應用服務層ApplicationService
private readonly ICustomerAppService _customerAppService;
public CustomerController(ICustomerAppService customerAppService,
INotificationHandler<DomainNotification> notifications) : base(notifications)
{
_customerAppService = customerAppService;
}
但在項目中,為了簡化開發量,表現層調用應用服務層的實現類(違反了面向介面編程)。表現層在CustomerController控制器中如下所示:
//調用應用服務層ApplicationService
private readonly CustomerAppService _customerAppService = null;
//日誌對象
public readonly ILoggerEX _logger;
public CustomerController(INotificationHandler<DomainNotification> notifications,
ILoggerEX logger,
CustomerAppService customerAppService): base(notifications)
{
_customerAppService = customerAppService;
_logger = logger;
}
(2)依賴倒置
是一種編程風格,是面向介面編程的實現,依賴倒置的應用如:DDDCQRS, 層依賴於層介面,而不是層依賴於另一個層的實現。依賴注入模式是依賴性反轉的常見實現。依賴性反轉方法提供了靈活性,可以幫助實現可插入設計,因為依賴性是通過配置而不是程式碼組成的。它還可以最大化可測試性,因為您可以輕鬆地將具體測試類注入到設計的不同層中。
依賴性是通過配置的,如下程式碼所示,由CommandRepository類來實現ICommandRepository介面:
services.AddScoped<ICommandRepository<CommandModels.Customer>, CommandRepository<CommandModels.Customer>>();
services.AddScoped<IQueryRepository<QueryModels.Customer>, QueryRepository<QueryModels.Customer>>();
(3) 基於消息
可以使用基於消息的通訊來實現介面並提供層之間的交互,.net技術如:wcf, web services, msmq它們支援跨物理和進程邊界的交互(以xml的soap格式傳輸),但這是對於09年流行的web架構。現在基於消息多數用web api技術,是面向微服務開發(以json的rest api)。
參考資料