對於商務邏輯的組織,個人認為,最好是使用 DDD(《Domain Driven Design》) 的方式。DDD 使用領域模型來表達實體間的關係,同時在應用程式層使用 Service 來組織各實體間的過程式代碼。二者構成了整個應用程式的核心商務邏輯(《Pattern of Enterprise Application Architecture》)。
OEA 是一個基於 DDD 思想的架構。在 OEA 中,使用了 Service、Controller 來組織過程式邏輯。結構如:
對於大型系統來說,OEA 中的 Service 主要作為分布式調用、本地調用的 Facade 介面,主要的業務過程則使用 Controller 來編寫。對於小型系統來說,則可以直接把業務過程邏輯都編寫在 Service 中。
在設計 Controller 時,應該特別注意兩點:
* 擴充點:Controller 中表達業務過程行為的過程式方法,可以被擴充。這種擴充不應該改動調用方的代碼。
* 單向依賴:Controller 之間應該是單向依賴的。否則,將會造成商務邏輯混亂。
我以最近編寫的一個倉庫管理產品的類圖,來說明如何設計,能更好地達到以上兩點:
該倉庫管理產品的商務邏輯使用 Controller 組織。在編寫完成產品後,可以編寫擴充程式集,為產品主幹程式集中的商務邏輯編寫擴充。
Client:主幹程式集中的用戶端程式,它調用服務完成分布式調用邏輯。
Service:主幹程式集中的服務程式,它調用工廠建立 ReceiveController 來間接完成入庫邏輯。
ReceiveController:主幹程式集中的入庫業務控制器,它會組織入庫相關的各個領域模型(如倉庫、貨品等),來完成相關業務。
ReceiveControllerExt:擴充程式集中的入庫業務控制器。它繼承自主幹程式集中的 ReceiveController,並重寫了基中的 Receive 方法,提供了新的入庫商務邏輯。
MoveController:主幹程式集中的移庫業務控制器。它依賴入庫控制器,需要在入庫業務控制器中貨品到達後,執行它指定的移庫邏輯。入庫控制器不能依賴移庫控制器,這樣,某些情境下,就可以把移庫控制器去除,以達到簡單入庫、不執行移庫邏輯的目的。
OEA.Controller: 架構提供的控制器基類,“層基類模式”。
OEA.ControllerFactory:架構提供的控制器工廠。使用原廠模式封裝了所有業務控制器的構造過程,提供以下功能:
1. 具體控制器的建立。
建立具體子類的控制器,而不需要修改調用方代碼。例如:當 Service 指定構造 ReceiveController 時,如果已經載入了 ReceiveControllerExt 類型擴充,則 ControllerFactory 會返回 ReceiveControllerExt 類型的執行個體,使得執行被擴充後的商務邏輯。
2. 控制器事件的自動掛接。
控制器聲明所依賴的其它控制器,架構會自動調用其相關的掛接程式。例如:MoveController 依賴 ReceiveController,並使用 ControllerFactory 中的方法來聲明需要監聽 ReceiveController 中的 Received 事件。則 ControllerFactory 在建立 ReceiveController 時,也會建立一個 MoveController 的執行個體,並使其掛接到 ReceiveController.Received 事件上。這樣就不需要改動 ReceiveController 的代碼。
其實,整個設計主要是使用“簡單原廠模式”來封裝了業務控制器的構造過程,而達到擴充的效果。
不過由於在物件導向設計中,虛方法擴充、事件擴充是最常用的擴充設計(《Framework Design Guidelines 2nd Edition》),而同時業務控制器的設計基本上都需要這兩類擴充,所以總結一下這個常用的控制器設計,以方便使用。
--------------------------------
附,使用此方案後,整個倉庫系統中 Controller 的重構成果如下。解耦前:
解耦後:
簡化圖,解耦前:
解耦後: