引自:《Software Architecture Patterns》
附腦圖
分層架構
分層架構(layered architecture)是最常見的軟件架構,也是事實上的標准架構。
- 解耦方式:每一層都有清晰的角色和分工,而不需要知道其他層的細節。
- 通訊方式:層與層之間通過接口通信。
架構模型
通常結構可分為4個標准層級,各層級的定義如下:
- 表現層(presentation):用戶界面,負責視覺和用戶互動
- 業務層(business):實現業務邏輯
- 持久層(persistence):提供數據,SQL 語句就放在這一層
- 數據庫(database) :保存數據
So why not allow the presentation layer direct access to either the persistence layer or database layer? After all, direct database access from the presentation layer is much faster than going through a bunch of unnecessary layers just to retrieve or save database information. The answer to this question lies in a key concept known as layers of isolation.
The layers of isolation concept means that changes made in one layer of the architecture generally don’t impact or affect components in other layers: the change is isolated to the components within that layer, and possibly another associated layer (such as a persistence layer containing SQL).
The layers of isolation concept also means that each layer is independent of the other layers, thereby having little or no knowledge of the inner workings of other layers in the architecture.
層級隔離的設計理念,使得層與層之間實現了高內聚、低耦合。
Creating a services layer is usually a good idea in this case because architecturally it restricts access to the shared services to the business layer (and not the presentation layer). Without a separate layer, there is nothing architecturally that restricts the presentation layer from accessing these common services, making it difficult to govern this access restriction.
層級架構也使得增加新的業務層更加容易——因為層級(模塊)間的耦合度很低,新的層級只需要處理層與層之間的接口就OK了。
示例
調整業務結構,我們增加一個新的層級——服務層。
The services layer in this case is marked as open, meaning requests are allowed to bypass this open layer and go directly to the layer below it.
In the following example, since the services layer is open, the business layer is now allowed to bypass it and go directly to the persistence layer, which makes perfect sense.
由於新的層級是開放的而非閉合的,它允許上游層越過自身,實現直接對下游層數據的訪問。
小結
分層架構可以直觀的實現不同層級邏輯代碼的解耦合,除此之外:
- 結構簡單,容易理解和開發
- 不同技能的程序員可以分工,負責不同的層,天然適合大多數軟件公司的組織架構
- 每一層都可以獨立測試,其他層的接口通過模擬解決
缺點:
- 一旦環境變化,需要代碼調整或增加功能時,通常比較繁雜和費時
- 部署很麻煩:即使只修改一個小地方,往往需要整個軟件重新部署,不容易做持續發布
- 軟件升級時,可能需要整個服務暫停
- 擴展性差:用戶請求大量增加時,必須依次擴展每一層,由於每一層內部是耦合的,擴展會很困難
模式分析:
- 總體靈活性: 低
- 發布易用性: 低
- 可測試性: 高
- 性能: 低
- 規模擴展性: 低
- 開發容易度: 高
事件驅動架構
事件驅動架構(Event-Driven Architecture)是一個流行的分布式異步架構模式。基於這種架構模式應用可大可小。它由高度解耦、單一目的的事件處理組件組成,可以異步地接口和處理事件。
事件:系統或組件的狀態發生變化時,系統層發出的通知。
解耦方式:每個對象都通過與中間件(Mediator or Broker)實現與外界的溝通,而不是相互依賴(迪米特法則)。
通訊方式:以消息為載體、通過中間件傳遞通訊。
拓撲結構 - 分類
The event-driven architecture pattern consists of two main topologies, the mediator and the broker. The mediator topology is commonly used when you need to orchestrate multiple steps within an event through a central mediator, whereas the broker topology is used when you want to chain events together without the use of a central mediator. Because the architecture characteristics and implementation strategies differ between these two topologies, it is impor‐tant to understand each one to know which is best suited for your particular situation.
Broker拓撲架構
相比於后面講到的Mediator,Broker是一種更本質也更簡潔的EDA(不知道作者為何選擇先講解復雜的Mediator,個人認為應該將這個順序調整過來)。
該結構包含了三個組件:
- Event:事件消息;
- Event Channel:消息隊列或者是Topic,根據訂閱推送(或是轉發)消息;消息可能被轉發到Event Processor,或是其他的Event Channel中。
- Event Processor:獲得消息后執行處理。它可能是一個消息的處理終結點,也可能在處理過程中繼續下發消息,或生成新的事件,並被再次提交到Event Channel中,繼續下一輪的轉發和處理。
示例
如上圖:當系統中的某個對象觸發了某個事件,事件被提交到Custormer Process,然后派送到Broker。Broker將解析事件並轉發到相關的Processor中執行處理(注意,兩個Processor是各自獨立進行的,不存在任何耦合)。而在Processor中又觸發了新的事件(“責任鏈”模式),並通過各自的Channel轉發到另外的Processor中完成處理。
在Mediator中,我們將看到Mediator模式對該問題的另外一種處理方式。
Mediator拓撲結構
Mediator可以說是Broker的一種拓展,更適用於粗粒度事件的處理——由於業務邏輯更復雜,細粒度的拓撲結構將造成消息臃腫而混亂,當然,粗粒度也增加了EDA架構的復雜度。
采用Mediator模式的架構中,事件一般是復雜的(包含多個執行單元的合集),而Mediator的責任就是將該復合事件拆解為獨立的子事件,然后發送到不同類型的子事件處理系統中,由子系統完成獨立子事件的分發和處理。
在結構上,Mediator的EDA架構包括4個組件:
- event queues:用於原始事件(分類)存儲,並轉發給Event Mediator,一般是以MQ的形式存在。
- event mediator:用於原始事件的拆解(成為多個獨立子事件),並轉發給相關的Channel;
- event channels:通道(可以理解為細分的Event Queue),按照事件的類型不同作以划分。它可以是一個消息隊列,提供給特定的Processor查詢,或是消息Topic,發送特定的廣播。
- event processors:事件處理器,監聽特定的Channel,並在捕獲事件后進行處理
在邏輯上,Mediator的處理過程如下:
The event flow starts with a client sending an event to an event queue, which is used to transport the event to the event mediator. The event mediator receives the initial event and orchestrates that event by sending additional asynchronous events to event channels to execute each step of the process. Event processors, which listen on the event channels, receive the event from theevent mediator and execute specific business logic to process the event.
It is common to have anywhere from a dozen to several hundred event queues in an event-driven architecture. The pattern does not specify the implementation of the event queue component; it can be a message queue, a web service endpoint, or any combination thereof.
EDA對 Event Queues 並沒有特定的實現要求,無論是數量還是類型,都可以根據實際需要確定。
There are two types of events within this pattern: an initial event and a processing event. The initial event is the original event received by the mediator, whereas the processing events are ones that are generated by the mediator and received by the event-processing components.
對應於 Event Queues 和 Event Channel,也同樣存在着兩種事件類型:原始事件(或者稱為:復合事件)和處理事件。
呃,這句話倒過來說更合適:因為有不同的事件類型,所以需要對應存在不同的隊列。
Event channels are used by the event mediator to asynchronously pass specific processing events related to each step in the initial event to the event processors. The event channels can be either message queues or message topics, although message topics are most widely used with the mediator topology so that processing events can be processed by multiple event processors (each performing a different task based on the processing event received).
The event processor components contain the application business logic necessary to process the processing event. Event processors are self-contained, independent, highly decoupled architecture components that perform a specific task in the application or system. While the granularity of the event-processor component can vary from fine-grained (e.g., calculate sales tax on an order) to coarsegrained (e.g., process an insurance claim), it is important to keep in mind that in general, each event-processor component should perform a single business task and not rely on other event processors to complete its specific task.
事件處理器包含實際的業務邏輯。每個消息處理器都是自包含的,獨立的,高度解耦的,執行單一的任務(高內聚低耦合的要求使然)。這部分是我們開發和拓展業務的主要戰場。
有一些開源的框架實現了這種架構,如Spring Integration, Apache Camel, 或者 Mule ESB。
當然,這種架構包含了多種形式的變種,你應當能夠根據需要,靈活的替換相應的子模塊以適配需求。
示例
還是剛才的Move的例子,在Broker的架構中,事件以遞歸方式,實現自包含,通過Processor與Channel的互相調用完成消息的傳遞。這是所謂“責任鏈”模式的體現。
而Mediator則是通過統一的封裝性,將本身的Move事件封裝成若干子事件,每個子事件由不同的Event Channel在子系統內實現派發。這樣的模式效率無疑是更高的,但它要求固定的問題域——這就導致其可拓展性較差,一旦體系需要拓展,或原Event結構出現變化,子系統也必須全盤修改。
小結
架構考量:
事件驅動架構模式實現起來相對復雜,主要是由於它的異步和分布式特性。這可能會帶來一些分布式的問題,比如遠程處理的可用性,缺乏響應,broker重連等問題。
優點:
- 分布式的異步架構,事件處理器之間高度解耦,軟件的擴展性好
- 適用性廣,各種類型的項目都可以用
- 性能較好,因為事件的異步本質,軟件不易產生堵塞
- 事件處理器可以獨立地加載和卸載,容易部署
缺點:
- 涉及異步編程(要考慮遠程通信、失去響應等情況),開發相對復雜
- 難以支持原子性操作,因為事件通過會涉及多個處理器,很難回滾
- 分布式和異步特性導致這個架構較難測試
模式分析
- 總體靈活性: 高
- 發布易用性: 高
- 可測試性: 低
- 性能: 高
- 規模擴展性: 高
- 開發容易度: 低
微內核架構
微核架構(microkernel architecture)又稱為"插件架構"(plug-in architecture),指的是軟件的內核相對較小,主要功能和業務邏輯都通過插件實現。
內核(core)通常只包含系統運行的最小功能。插件則是互相獨立的,插件之間的通信,應該減少到最低,避免出現互相依賴的問題。
解耦方式:業務相關項以插件的形式發布,可以選擇動態加載。業務插件只與內核交互,實現業務間的低耦合。
通訊方式:插件與內核,通過內核發布的特定接口進行通訊(通訊樣式無限制)
架構模型
The microkernel architecture pattern consists of two types of architecture components: a core system and plug-in modules. Application logic is divided between independent plug-in modules and the basic core system, providing extensibility, flexibility, and isolation of application features and custom processing logic.
由於許多的系統都是采用的類似架構設計,該架構模式因此得名。
The microkernel architecture pattern consists of two types of architecture components: a core system and plug-in modules. Application logic is divided between independent plug-in modules and the basic core system, providing extensibility, flexibility, and isolation of application features and custom processing logic.
內核系統是一種最小化開發的、能夠保證程序基本運行的應用。一般不包含業務層,基本的業務邏輯也是抽象化的通用性規則。而具體規則和業務邏輯,則通過不同的插件拓展實現。
The plug-in modules are stand-alone, independent components that contain specialized processing, additional features, and custom code that is meant to enhance or extend the core system to produce additional business capabilities. Generally, plug-in modules should be independent of other plug-in modules, but you can certainly design plug-ins that require other plug-ins to be present. Either way, it is important to keep the communication between plug-ins to a minimum to avoid dependency issues.
想要插件之間完全沒有耦合是不合理的,但盡可能的減少插件之間的耦合,關系到整個體系的運轉與維護。
The core system needs to know about which plug-in modules are available and how to get to them. One common way of implementing this is through some sort of plug-in registry. This registry contains information about each plug-in module, including things like its name, data contract, and remote access protocol details (depending on how the plug-in is connected to the core system). For example, a plug-in for tax software that flags high-risk tax audit items might have a registry entry that contains the name of the service (AuditChecker), the data contract (input data and output data), and the contract format (XML). It might also contain a WSDL (Web Services Definition Language) if the plug-in is accessed through SOAP.
一般的,插件通過注冊機制掛載到內核上。內核將維持一個插件列表,以了解當前系統中的每一個組件。插件的掛載方式可以有多種,例如通過 dlopen( ) 將lib庫載入內核進程內存;或是通過 SOA 模式加載服務項,並執行服務調用;或是通過點對點 socket 建立 romote object proxy 的隊列,並執行RPC調用,等等。更多的方式見下一段:
Plug-in modules can be connected to the core system through a variety of ways, including OSGi (open service gateway initiative), messaging, web services, or even direct point-to-point binding (i.e., object instantiation). The type of connection you use depends on the type of application you are building (small product or large business application) and your specific needs (e.g., single deploy or distributed deployment). The architecture pattern itself does not specify any of these implementation details, only that the plug-in modules must remain independent from one another.
插件架構模式本身並沒有限定core與module的連接方式——連接方式一般需要根據具體的應用場景選擇不同的方案。
The contracts between the plug-in modules and the core system can range anywhere from standard contracts to custom ones. Custom contracts are typically found in situations where plug-in components are developed by a third party where you have no control over the contract used by the plug-in. In such cases, it is common to create an adapter between the plug-in contact and your standard contract so that the core system doesn’t need specialized code for each plug-in. When creating standard contracts (usually implemented through XML or a Java Map), it is important to remember to create a versioning strategy right from the start.
插件之間的通信方式也可以有多種實現,一般的建議使用通用協議,如HTTP或標准socket消息結構。
小結
架構考量:
微內核的架構模式可以嵌入到其它的架構模式之中。微內核架構通過插件還可以提供逐步演化的功能和增量開發。所以如果你要開發基於產品的應用,微內核是不二選擇。
模式分析:
- 總體靈活性: 高
- 發布易用性: 高
- 可測試性: 高
- 性能: 高
- 規模擴展性: 低
- 開發容易度: 低
優點:
- 良好的功能延伸性(extensibility),需要什么功能,開發一個插件即可
- 功能之間是隔離的,插件可以獨立的加載和卸載,使得它比較容易部署,
- 可定制性高,適應不同的開發需要
- 可以漸進式地開發,逐步增加功能
缺點:
- 內核擴展性差:內核通常是一個獨立單元,不容易做成分布式
- 開發難度相對較高,因為涉及到插件與內核的通信,以及內部的插件登記機制