軟體架構分層方法論

2020-09-28 09:10:19

一般初創軟體,為快速上線,幾乎不考慮分層。但隨業務越發複雜,就會導致邏輯複雜、模組相互依賴、程式碼擴充套件性差等各種問題。

架構分層迫在眉睫。

1 什麼是架構分層?

軟體工程中常見的設計方式,將整體系統拆分成N個層次,每個層次有獨立的職責,多個層次協同提供完整的功能。

初學 JavaWeb 時一般要求設計成 MVC 架構。另外一種常見的分層方式是將整體架構分為

  • 表現層(Web)
    展示資料結果和接受使用者指令的,是最靠近使用者的一層;
  • 邏輯層(Service)
    複雜業務的具體實現;
  • 資料存取層(Dao)
    主要處理和儲存之間的互動。

這就可以隔離關注點,讓不同的層專注做不同的事情。其它分層案例,比如OSI網路七層模型,TCP/IP協定網路四層模型。

2 分層有什麼好處?

簡化設計

各司專職,而不必將自己活成全才。

高複用

比如在設計某系統時,發現某層具有通用性,就可把它抽取獨立出來,在設計其它系統時使用。

橫向擴充套件

可以讓我們更容易做橫向擴充套件。如果系統沒有分層,當流量增加時我們需要針對整體系統來做擴充套件。但是,如果我們按照上面提到的三層架構將系統分層後,就可以針對具體的問題來做細緻的擴充套件。

比如業務邏輯裡面包含有比較複雜的計算,導致CPU成為效能的瓶頸,那這樣就可以把邏輯層單獨抽取出來獨立部署,然後只對邏輯層來做擴充套件,這相比於針對整體系統擴充套件所付出的代價就要小的多了。

架構分層究竟和高並行設計的關係是怎樣的?我們知道橫向擴充套件是高並行設計思想之一,既然架構分層可方便橫向擴充套件, 那麼高並行系統一定是分層的。

3 如何架構分層?

關鍵在於理清層次邊界。
若按三層架構分層,邊界不就很容易界定嗎?
是的沒毛病,當業務邏輯簡單時,層次間邊界的確清晰,開發新功能時也知道把程式碼往哪寫。但當業務邏輯越來越複雜,邊界也會變得模糊。

3.1 案例

任一系統都有使用者模組,比如返回使用者資訊的介面,它呼叫邏輯層的GetUser方法,GetUser方法又和User DB互動獲取資料,就像下圖左邊展示的樣子。

這時,產品提出一個需求,在APP中展示使用者資訊的時候,若使用者不存在,那麼要自動給使用者建立一個使用者。同時,要做一個HTML5頁面保留之前的邏輯,即不需要建立使用者。這時邏輯層的邊界就變得不清晰,表現層也承擔了一部分的業務邏輯(將獲取使用者和建立使用者介面關聯)。

3.2 主流分層職責

MVC架構的簡單性讓太多的人覺得專案工程結構理所應當就是這樣的,然後呢,一大堆的業務邏輯就隨意的堆砌在了service中,物件啥的,只是單純的資料傳輸作用,出現了用物件導向的語言,寫程式導向的程式的普遍現象。按照領域驅動設計的思路,最重要的還要有領域模型層。當然manage層這種方案也是一種思路,但是我覺得,這種方式,還不夠,必須有清晰的業務模型和合理的分層結構配合,才能更好的提現分層的作用。

終端顯示層

各端模板渲染並執行顯示的層。當前主要是 Velocity/framemaker渲染、js渲染、行動端展示等

開放介面層

將Service層方法封裝成開放介面,同時進行閘道器安全控制和流量控制等

Web層

主要是對存取控制進行轉發,各類基本引數校驗,或者不復用的業務簡單處理等

Service層

業務邏輯層。呼叫manager層和dao層處理業務,即簡單的業務可以不抽取manager,直接呼叫dao。
業務類的校驗放在service層,一般性的引數校驗可以放在web層,這可以通用化。

Manager 層(通用業務處理層)

DDD中也叫領域層。

  1. 可以將原先Service層的一些通用能力下沉到這一層,比如與快取和儲存互動策略,中介軟體的接入。業務邏輯放在manager,service來編排manager的原子服務。
  2. 也可在這一層封裝對第三方介面的呼叫,比如呼叫支付服務,呼叫稽核服務等

該分層架構相比MVC主要就是增加了Manager層,它與Service層的關係:Manager層提供原子的服務介面,Service層負責依據業務邏輯來編排原子介面。

  • 業務邏輯就是你的產品邏輯,比如建立使用者要具體做什麼。
  • manager層的原子服務指的是實現單一功能的服務。
  • 事務應該在service層

DAO層(資料持久層)

資料存取層,與底層 MySQL、Oracle、HBase 等進行資料互動。快取可以放在儲存層。

外部介面或第三方平臺

包括其它部門 RPC 開放介面,基礎平臺,其它公司的 HTTP 介面

例如,Manager層提供建立使用者和獲取使用者資訊的介面,Service層負責將這兩個介面組裝起來。這樣就把原先散佈在表現層的業務邏輯都統一至Service層,每層邊界就清晰了。

比如做一個介面,可將實現放在service層。之後公司內部呼叫邏輯可放在web層。而哪一天公司要開放這個介面,可直接新抽象一層(一個新的服務),即開放平臺層!這樣的好處是,可以將本司使用和第三方使用做隔離。比如在提供服務時,為了保證自家介面效能,對開放平臺層做限流處理。

傳統公司很多是分層部署的,比如保險和金融。service和dao部署在比較嚴密的網路區域,controller層部署在一個較寬鬆的網路區域,對外提供服務。等於在網路上增加了一個緩衝區,來保證服務的安全;而且可以通過單向網路規範層級呼叫,controller可以呼叫服務層,而服務層是不能呼叫web層的。
如果將資料存取層單獨部署,比如拆分為單獨的rpc服務,當然這樣拆分粒度比較細。controller就是對外的門面,呼叫單獨的服務層

  • 可以為後期服務運維降低成本
  • 可以提高資料存取層的複用度(資料存取層對外提供API,其他層的應用通過API方式與資料庫進行互動),三來可以遮蔽各個資料庫實現的具體細節。

3.3 層間依賴

資料流轉只能在相鄰層間。以三層架構為例,資料從表示層進入後一定要流轉到邏輯層,做業務邏輯處理,然後流轉到資料存取層和DB互動。但若業務邏輯很簡單,可否從表示層直接到資料存取層,甚至直接讀DB?
功能上是可以的,但從長遠架構設計考慮,這會造成層級呼叫混亂,比如一旦DB地址變更,就需更改多層,這就失去了分層價值,且維護或重構都是災難。

4 架構分層的缺陷

4.1 增加程式碼複雜度

原本可在接收請求後直接查詢DB獲得結果,卻非要在中間多層設計,每層只簡單地做資料傳遞。
有時增加一個小需求,可能還需更改所有層的程式碼,增加了開發成本,也增加了偵錯複雜度,增加了與其它模組負責人的溝通成本。

4.2 效能損耗

若每層獨立部署,層間通過網路互動,那多層架構勢必會在效能上有所損耗。

那是否還要選擇架構分層呢?肯定的。

你要知道,任何的方案架構都是有優勢有缺陷的,天地尚且不全何況我們的架構呢?分層架構固然會增加系統複雜度,也可能會有效能的損耗,但是相比於它能帶給我們的好處來說,這些都是可以接受的,或者可以通過其它的方案解決的。我們在做決策的時候切不可以偏概全,因噎廢食。

5 總結

分層架構是軟體設計思想的外在體現,是一種實現方式。一些軟體設計原則都在分層架構中有所體現。

比方單一職責原則規定每個類只有單一的功能,在這裡可引申為每層擁有單一職責,且層與層之間邊界清晰;
迪米特法則原意是一個物件應當對其它物件儘可能少的瞭解,在分層架構的體現是資料的互動不能跨層,只能在相鄰層之間進行;

開閉原則要求軟體對擴充套件開放,對修改關閉。它的含義其實就是將抽象層和實現層分離,抽象層是對實現層共有特徵的歸納總結,不可修改,但具體實現可無限擴充套件,隨意替換。

參考

  • 《阿里巴巴Java開發手冊》