吳松森,陳曉陽,高春陽
(洛陽電光設備研究所,河南 洛陽 471000)
隨著嵌入式系統在航空、航天、航海等領域的應用不斷拓展,軟件定義系統已成為一種趨勢,未來嵌入式軟件必然會承擔越來越多的使命,同時也將面臨越來越多的問題。現有嵌入式軟件的規模急劇提升,而軟件的復雜度與軟件規模呈非線性正相關,在超10萬行的軟件規模下,已經引起了軟件復雜度的“質變”,為軟件開發與維護帶來巨大困難。同時,為不斷適應新的使用場景以及平臺能力持續提升,嵌入式軟件的需求頻繁變更,部分產品軟件甚至可每年變更數十次以上,在愈發龐大的軟件規模體量下,任何軟件變更都可能帶來巨大的工作量與不可預知的改動風險,從而延伸出了以下問題:(1)傳統的軟件開發模式已不再適用,相似功能重復性開發帶來巨大的人力開銷,導致軟件開發人員與軟件任務之間的矛盾更加激化;(2)在軟件復雜度不斷提升的情況下,軟件質量隱患逐漸加大,軟件開發人員水平對軟件質量的影響更加凸顯,軟件維護成本增加;(3)由于軟件規模增大,在沒有解決軟件內部的復雜耦合關系之前,新增功能的影響域增大,新功能的集成、驗證成本增加。
良好的軟件架構可以在軟件設計之初就規范好組件間接口、組件間關系、組件與環境之間的關系,能夠有效降低系統復雜性所帶來的各種影響。針對復雜嵌入式系統,如何建立一套高內聚、松耦合、易復用、能有效適應需求變化的軟件架構,成為本文亟待研究的問題。
復雜嵌入式系統軟件架構要解決的主要問題可以概括為以下3點:(1)復雜度分解問題;(2)面對需求變化的適應性與靈活擴展能力問題;(3)成熟功能的移植復用問題。針對上述問題,本文結合領域驅動設計技術、面向服務架構、微服務架構等進行分析研究,致力于尋求一種解決軟件復雜度問題的有效方案。
領域驅動設計(Domain-Driven Design,DDD)[1]是在2004年提出的一種開放的設計方法體系,其核心思想在于根據“領域”進行復雜問題拆分,總的原則是將原來復雜的問題拆分成多個單獨的簡單問題進行求解。DDD強調業務邏輯與技術實現分離[2],更加關注業務復雜度分解以及業務上下文邊界的界定,對于解決復雜的軟件問題具有重要參考價值。
1.1.1 DDD分層架構
典型的DDD分層架構如圖1所示,該架構遵循了“關注點分離”原則[3],將屬于業務邏輯的關注點放到領域層(Domain Layer)中,而將支撐業務邏輯的技術實現放到基礎設施層(Infrastructure Layer)中,應用層確定了兩者之間的邊界,通過依賴注入(Dependency Injection,DI)的方式將二者有機結合起來。

圖1 DDD分層架構
1.1.2 六邊形架構
由Alistair Cockburn提出的六邊形架構如圖2所示[4],通過由內向外“剝洋蔥式”逐級分離的方式,將核心領域業務放在了架構的核心位置,應用層作為領域核心與外圍適配器的轉換,由內向外依次從“穩定”到“易變”。該架構通過端口適配的方法[5],讓外部易變的部分依賴于逐層穩定的模型,間接保證核心業務代碼不受外部變化的影響,從而提升了系統靈活性。

圖2 典型的六邊形架構
面向服務架構(Service-Oriented Architecture,SOA)[6-7]將應用程序的不同功能單元進行拆分,通過一系列接口和統一的協議來進行數據交互和互相依賴。SOA架構的核心思想是互操作、復用、松耦合。DDD更注重業務邏輯與技術實現分離以及業務邏輯層面的解耦,SOA更注重于功能的服務化以及良好接口設計,二者并不沖突,甚至可以有機結合,實現更好的效果。面向服務設計的思想對于實現功能單元界面劃分及可復用功能組件具有非常大的借鑒意義。
微服務架構將應用程序構建為松耦合、可獨立部署的一組服務[8-9]。相對比SOA思想,微服務架構對服務層進行細粒度的拆分,所拆分的每個服務只完成某個特定的業務功能,服務的粒度很小,所以稱為微服務[10]。由于服務顆粒度細、職責單一,微服務更加有利于資源的重復利用。對于外部交聯關系復雜的嵌入式系統,往往存在多種對外通信方式,如離散量、模擬量、FC總線、CAN總線、RS422總線等,因此非常適用借鑒微服務架構思想,為每種通信方式實現一種微服務,通過多種微服務組合支撐系統功能實現。
上述架構的對比分析結果為復雜嵌入式系統的軟件架構設計提供了可供借鑒的思路,具體如表1所示。

表1 軟件技術分析對比
不同利益相關角色對于軟件架構的訴求和關注點各不相同。用戶更關注產品運行的可靠性和易維護性;產品的總體設計人員要求軟件架構能夠快速完成高質量的軟件開發,對需求有良好的適應性,同時出現產品故障時應能快速定位與解決;軟件需求人員更關心軟件需求與軟件架構之間追溯關系清晰明確,能夠支持關鍵需求的覆蓋及實現;對于軟件開發人員,軟件架構應該內層次關系清晰明確,能夠支持軟件設計人員進行軟件設計,減少設計工作量;軟件測試人員則要求軟件架構具備易讀性、可觀測性,功能、接口明確,方便測試。
結合上述設計目標,為滿足業務場景、利益攸關方等需求,復雜嵌入式系統軟件的非功能需求可定義如下。(1)開放性:架構應具備良好的開放性,各功能模塊松耦合、高內聚,能夠很好地適應變化、支持擴展。(2)服務化:架構應具有良好的服務化設計,能夠為新功能集成提供穩定的服務化接口,支持新功能快速集成。(3)通用化:架構中的基礎設施、服務化組件、應用層組件等應具有一定的通用化設計,支持跨產品移植復用。
綜合上述分析,要實現復雜嵌入式系統下通用軟件架構,本文擬采用領域驅動設計思想,結合領域專家業務經驗以及面向對象的技術手段完成軟件的服務化實現。具體設計思想可以概括如下。
(1)分治思想。分而治之思想是領域驅動設計思想中一項核心設計思想,就是把復雜問題分解為若干簡單問題來簡化。采用分治思想,會使每個小功能模塊更加容易開發且不易出錯,同時能夠支持多人分布開發,提升開發效率。主要原則如下:采用分層設計,使不同業務關注點分離;遵循“單一職責”原則,應按照模塊、類、函數等逐層分解,最小原子程序只完成一個功能;經分割后的兩個功能模塊之間的依賴關系應簡單、穩定、弱相關;對于難以劃分歸屬關系的功能,優先從適應變化方面考慮。
(2)適應變化。在嵌入式業務領域,一個最大的特點就是“變化”,因此,通用軟件架構必須能夠適應變化。主要原則如下:新增功能帶來的變化應不影響原有軟件功能,最好不涉及任何原有軟件代碼更改;任何功能模塊對新增需求應具有良好的擴展性,最好只增加代碼,不更改代碼;任何需求變更的影響域應相對集中,最好一個需求變更應只影響一個功能模塊,不影響其他功能模塊。
基于上述設計思路,結合復雜嵌入式系統特點,本文采用分層設計[11-13]將系統軟件架構分解為應用層、服務層、資源層,同時整體架構依賴于基礎設施實現系統功能,軟件架構如圖3所示。

圖3 軟件架構
資源層主要用于部署與運行平臺相關的資源組件,包括操作系統、運行時庫、離散量驅動、總線驅動、文件系統驅動等,通過完成驅動集成與封裝,為應用層、服務層提供穩定的資源訪問接口。一方面,資源層對上訪問接口的統一封裝有利于更換硬件平臺時不影響應用層及服務層軟件,另一方面,根據測試先行的設計理念,嵌入式軟件在開發時需持續開展測試,資源層可兼顧全數字仿真環境下的測試激勵注入,有利于擺脫硬件平臺對核心邏輯測試的約束,更好地完成持續集成、持續測試。
服務層主要用于部署服務組件,向下通過資源層屏蔽與底層操作系統之間的耦合,向上通過服務接口向應用組件提供功能服務,是架構中的核心組成部分,包括領域服務和基礎服務兩種。(1)領域服務組件與特定應用領域相關,通過對系統中特定領域核心邏輯的抽象,確定領域內、外部功能剖面及統一的服務接口,通過可重用化設計方法提取共性,具備一定的跨平臺移植的能力。領域服務組件可調用基礎服務,包括但不限于軟件版本服務、故障處理服務、健康管理服務等。(2)基礎服務組件要用于為其他模塊提供基礎服務,包括但不限于以下服務。總線服務:用于對外提供FC總線、CAN總線、RS422總線等總線消息隊列的統一管理、消息重發機制、消息發送間隔控制機制等服務。離散量服務:用于對外提供離散量信號的抖動濾波、多路決策等功能。模擬量服務:用于對外提供模擬量信號的濾波、平滑等功能;定時器服務:用于對外提供定時器、時間獲取、時間間隔控制等功能;日志服務:用于對外提供系統運行過程中信息存儲與記錄功能。
應用層主要用于部署與具體外部接口相關業務邏輯相關的應用組件,完成系統的綜合處理與控制功能,向上實現外部接口與服務層之間的適配轉換,向下調用統一的服務接口,而不關心具體底層服務類接口的實現。相對于傳統的分層架構,本架構內對應用層進行了功能弱化,主要負責外部接口與核心業務邏輯(服務層)之間的適配。
基礎設施主要用于部署支持軟件框架運行的基礎工具,包括各組件管理調度機制、服務管理調度機制、事件總線管理機制、通用算法支持庫等,提供不同組件和服務的注冊、初始化以及運行調用,完成快速集成,同時通過事件總線管理機制來解耦各軟件模塊,為整個軟件架構提供底層的通用算法。基礎設施層主要用于實現軟件框架的質量特性,包括易讀性、維護性、擴展性等。
業務邏輯與技術實現分離的設計方法可以使軟件設計人員更加關注業務邏輯的解耦,對于實現軟件復雜度分解及適應需求變化非常有借鑒意義。技術實現是指實現一項業務功能的具體方法,比如:處理其他設備發送的消息,可以直接調用總線接收接口主動獲取數據,也可以通過訂閱-分發的方式被動獲取數據。通過抽象應用層技術實現的接口,依靠依賴注入的方法脫離具體實現細節,應用層的核心業務就會大大提升穩定性,在更換平臺或者需求發生變化時不會由于其他層的耦合特性從而導致核心代碼的修改。
事件總線機制[14]是對觀察者模式的一種技術實現,通過訂閱-分發方式來實現對數據以及事件的集中處理與分發,各應用組件通過訂閱接口對所關注的事件進行注冊。當事件處理中心監測到事件發生時,向各訂閱方進行事件的分發,從而能夠允許不同組件之間進行彼此通信而無需進行強依賴,從而達到降低耦合度的效果。除了系統外部接口觸發的事件外,系統內可以根據領域業務分解來抽象定義出若干內部事件,依托該機制實現各組件間的信息交互。
在進行軟件架構設計時,需要同時兼顧規范性和靈活性,而規范性與靈活性本身又存在著互相制約和互斥的關系。面向對象設計原則能夠幫助設計者在進行軟件架構規范性設計時,盡可能地提升軟件重用性、靈活性和擴展性。常用的面向對象設計原則包括單一職責原則、開閉原則、里式代換原則、依賴倒轉原則、接口隔離原則、合成復用原則、迪米特法則[15]。
結合嵌入式系統的應用發展趨勢,針對嵌入式系統復雜度不斷提升帶來的一系列問題,本文結合領域驅動及服務化設計思想,提出了一種高內聚、松耦合、易擴展、可移植的通用軟件架構設計方案,有助于實現系統軟件模塊解耦、可快速適應不同開發平臺、有助于分布式開發和獨立部署。該軟件架構已在多個項目與嵌入式設備上得到了充分驗證。實踐結果表明該軟件架構體系有較好的應用前景,可以有效解決系統復雜性的問題,同時能夠有效適應于需求頻繁變化的業務場景,在不影響原有代碼的基礎上快速完成新功能的增加或修改。