close

作者 | Karsten Silz

VMware 推出了一個實驗性的項目 Spring Modulith,以便於通過模塊和事件更好地組織 Spring Boot 3 應用。該項目引入了新的類和註解,但並不會生成代碼。它的模塊沒有使用 Java Platform Module System(JPMS),而是映射到了普通的 Java 包。模塊有 API,但是 Spring Modulith 鼓勵使用 Spring 應用事件作為「主要的交互方式」。這些事件可以自動持久化到事件日誌中。Spring Modulith 還簡化了模塊和事件的測試。

2022 年 11 月推出的 Spring Boot 3 會是 Spring Modulith 的基礎。所以它的基線是 Spring Framework 6、Java 17 和 Jakarta EE 9。Spring Modulith 是 Moduliths(其名字有個「s」後綴)項目的繼承者。該項目使用 Spring Boot 2.7,目前已經退役,只接收缺陷修正,直至 2023 年 11 月份。

Spring Modulith 引入了自己的模塊抽象,因為 Java 的包是沒有層級結構的。這也就是為何在如下的示例代碼中,來自 example.order.internal 包的 SomethingOrderInternal 類對所有其他的類都是可見的,而不僅僅局限於 example.order 包中的類:

Example└─ src/main/java ├─ example | └─ Application.java ├─ example.inventory | ├─ InventoryManagement.java | └─ SomethingInventoryInternal.java ├─ example.order | └─ OrderManagement.java └─ example.order.internal └─ SomethingOrderInternal.java

現在,Spring Modulith 不會因為違反模塊訪問規則而使 Java 編譯失敗。它使用單元測試來確保這一點:在上面的樣例中,如果另外一個模塊嘗試訪問模塊內部的類 SomethingOrderInternal,那麼 ApplicationModules.of(Application.class).verify() 將會執行失敗。Spring Modulith 依賴 ArchUnit 項目來實現這一功能。

Spring Modulith 鼓勵使用 Spring Framework 的應用事件實現模塊間的通信。它通過一個事件發布註冊中心(Event Publication Registry)對這些事件進行了增強,該註冊中心通過持久化事件確保了事件的交付。即便整個應用發生了崩潰,或者只有一個模塊接收到了事件,註冊中心依然能夠確保事件正常交付。該註冊中心支持不同的序列化格式,默認格式為 JSON。內置的持久化方法是 JPA、JDBC 和 MongoDB。

事件的測試也得到了增強。如下的樣例展示了新的 PublishedEvents 抽象如何幫助過濾接收到的事件,使其僅包含具有特定 ID 的 OrderCompleted 事件:

@Testvoid publishesOrderCompletion(PublishedEvents events) { var reference = new Order(); orders.complete(reference); var matchingMapped = events .ofType(OrderCompleted.class) .matchingMapped (OrderCompleted::getOrderId, reference.getId()::equals); assertThat(matchingMapped).hasSize(1);}

Spring Modulith 能夠在特定時段結束時(如每小時、每天或每周)自動發布像 HourHasPassed、DayHasPassed 和 WeekHasPassed 這樣的事件。這些中心化的時間流逝(Passage of Time)事件是一個非常便利的方案,能夠替代模塊中重複的帶有 cron 觸發器的 Spring @Scheduled 註解。

Spring Modulith 沒有包含用於協調事件的工作流、編排或協同組件,因為在這方面,Spring 生態系統已經提供了大量的可選方案。

Spring Modulith 使用了 Spring Framework 6 對可觀測性的嶄新支持,為模塊 API 的持續時間和事件處理自動創建 Micrometer span。Spring Modulith 還可以通過創建兩種類型的 AsciiDoc 文件實現模塊的文檔化,分別是用於描述模塊間關係的 C4 和 UML 組件圖,以及用於描述單個模塊內容(比如 Spring bean 和事件)的 Application Module Canvas。

InfoQ 採訪了 Spring Modulith 項目負責人、VMware 的 Spring Staff 2 工程師 Oliver Drotbohm。

InfoQ:微服務解決了單體的組織問題,比如各部門無法以相同的節奏發布。它們也有技術方面的優勢,比如能夠獨立擴展應用的不同組成部分以及使用不同的技術棧。當初你們為何決定改進單體?現在的原因又是什麼?

Oliver Drotbohm:Spring Cloud 項目很好地覆蓋了微服務架構。但是,我們不想讓團隊覺得僅僅因為技術平台能夠更好地支持某種架構風格,就催促他們採用該風格。我們希望用戶能夠感受到同等水準的支持,與他們決定採用何種架構無關。

也就是說,單體系統,也包括分布式系統中的單個元素,都有一些內部結構。在最好的情況下,這種結構會在整個系統的生命周期內不斷發展和演進。我們的目標是,在最糟糕的情況下,它至少不會發生意外地退化。Spring Modulith 有助於在單個 Spring Boot 應用中表述和驗證結構:驗證是否引入了違反架構的行為,隔離的集成測試模塊,模塊間交互的運行時可觀測性,文檔抽取等。

不過,時機非常重要。我們看到,直到三年前,分布式系統的趨勢都很明顯。實踐經驗表明,團隊往往會過度分解他們的系統。在開始的時候,採用單體組織方式會有它的益處,尤其是快速發生變化的領域:隨着對業務需求理解的深入,模塊的組織需要能夠更快速地進行調整。在單體應用中,這更易於實現。這就是我們在這方面恢復興趣,以便於在應用中實現模塊化結構的原因所在,而且這種興趣正在不斷強化。

InfoQ:在只有一個模塊的應用中,Spring Modulith 有什麼樣的作用呢?

Drotbohm:我還沒有見過內部只有一個邏輯模塊,但能夠提供真正有用特性的軟件。

InfoQ:現在有一些即存的結構化單體,比如領域驅動設計(DDD)或者六邊形(Hexagonal)架構。似乎 Spring Modulith 創造了一種新的方式,為什麼要這樣做呢?

Drotbohm:它並不見得是創建一種新的方式。我們借鑑了模塊的概念,多年以來,這個概念已經有其基本語義了,在 DDD 中也能發現它,可以作為組織限界上下文的方式。Spring Modulith 想要回答的問題是,開發人員該如何非侵入式地在應用代碼中表述這些領域模塊。所表述的結構允許框架在集成測試中提供幫助,並且能夠觀測應用等等。技術化的結構方式,例如洋蔥(Onion)和六邊形架構,也可以用於模塊,只不過它們會作為實現細節。正如 Dan North 所建議的那樣,我們希望領域能夠作為整個代碼組織的主要驅動力。

InfoQ:在 Java 9 中,Java Platform Module System(JPMS)的目標是為 Java 提供「可靠的配置」和「強封裝性」。JPMS 為何沒有滿足你們對模塊的要求呢?

Drotbohm:JPMS 的設計目標是模塊化 JDK,在這方面它確實做得非常好。也就是說,對於那些只想在 Spring Boot 應用中定義一些邏輯模塊的應用開發人員來說,它們的一些設計決策是很有侵入性的。比如,JPMS 要求每個模塊都是一個單獨的 JAR,而集成測試必須打包成一個單獨的模塊。這帶來了嚴重的技術開銷,尤其是如果我們有更簡單的方式實現這一點的時候。

換句話說,Spring Modulith 能夠在 JPMS 結構的項目中運行良好。如果你的項目能夠從 JPMS 模塊的各種高級分離技術中受益,那麼盡可以使用它。我們依然基於此增加了一些令人興奮的特性,比如在不同作用域內(完整模塊或整個模塊的子樹)運行集成測試的能力。

InfoQ:Spring Modulith 中的模塊與 DDD 中的限界上下文有何異同?

Drotbohm:在 DDD 中,模塊是限界上下文內部的一種結構方式。在微服務架構中,上下文通常對應可部署的服務,這可能會導致由多個模塊組成的獨立 Spring Boot 應用。在更加單體化的應用中,開發人員為了方便,通常會因為類型系統引入模塊間更強的耦合性。在這種架構中,允許開發人員使用重構工具來改變代碼的整體組織,並將變更作為一個整體來部署,而不需要複雜的 API 演進過程。但是,即便是在這種代碼組織形式下,也可以通過放鬆耦合、引入防腐和映射層來構建限界上下文。也就是說,我們重視的主要概念是所謂的應用模塊(Application Module),與開發人員在哪個層級將限界上下文用到他們的應用中無關。

InfoQ:在 Spring Modulith 中,模塊會向其他模塊暴露 API。但是,它們之間也可以通過所謂的「應用事件」來進行交互,文檔中將其建議為「主要的交互方式」。Spring Modulith 為何更推薦使用事件?

Drotbohm:從調用其他模塊的 Spring bean 切換至發布應用事件會帶來不少影響。首先,它能夠讓調用者不必了解被調用者的情況。如果調用其他模塊的 Spring bean 的話,這會造成對調用者組件的依賴,隨着要注入的外部 bean 的數量增加,複雜性也隨之增加。這導致的主要問題在於,當我們需要對調用組件進行集成測試的時候,這些外部 bean 必須全部都是可用的。當然,我們可以 mock 協作者,但這意味着實現和測試都需要對代碼如何組織、哪些方法被調用等問題有完整地了解。每增加一個要調用的組件都會增加組織的複雜性。另外,我們需要將系統作為一個整體來部署,這使得測試變得更加脆弱,因為所有的模塊都需要被啟動起來,模塊 A 的問題可能會導致模塊 B 的測試失敗。

相反,發布應用事件能夠解決這個問題,因為它能夠讓發布組件不必知道哪些組件應該被調用,這些組件甚至不需要確保在集成測試時是可用的。應用模塊的隔離測試能力是一個很重要的因素。這非常類似於採用消息發布作為分布式系統的集成方式,而不是對相關系統進行主動調用。這個過程不需要額外的基礎設施,因為 Spring Framework 已經提供了進程內的事件總線。

InfoQ:其他框架都有不同程度的代碼生成功能。例如,Angular 有可定製的 schematics 來生成少量的代碼,如模塊或組件。在 Spring Modulith 中,有代碼生成相關的計劃嗎?

Drotbohm:我們沒有這方面的計劃,Spring Modulith 僅支持從結構化的組織中生成 C4 和 UML 組件圖。

InfoQ:如何將現有的 Spring Boot 3 項目遷移到 Spring Modulith?

Drotbohm:我們已經非常小心地確保使用 Spring Modulith 的基本功能沒有任何侵入性。在其最基礎的情況下,假設你已經遵循默認的包組織約定,你甚至不需要修改你的生產代碼。你可以在測試範圍內將驗證庫添加到項目中,並在測試案例中應用已就緒的架構適配功能。

InfoQ:Spring Modulith 是一個實驗性項目。在生產中使用它的安全性如何?

Drotbohm:Spring Modulith 的前身是 Moduliths,目前該項目已經到了 1.3 版本,在過去的兩年中,它已經被多個項目用到了生產環境中。因此,實驗性狀態僅僅表明我們啟動了一個新的 Spring 項目。另外,與 Moduliths 相比,我們變更了一個默認值,想看一下社區對這種變化的反應。我們想對反饋做出快速的響應,避免受到內部 API 兼容性要求的限制,這是非實驗性項目所必須面對的限制。我們粗略的計劃是利用 Spring Boot 3.1 之前的時間來收集反饋,除非我們發現任何重大問題,否則會在 2023 年第二季度初將該項目晉升為非實驗性項目。

InfoQ:Spring Modulith 目前的版本是 0.1 M2。它未來的計劃是什麼呢?

Drotbohm:我們目前正在向 Spring 開發者介紹這個項目,收集反饋,並試圖將其納入到 1.0 版本中。與 Modulith 相比,我們已經增加了基於 JDBC 和 MongoDB 的事件發布註冊中心的實現。我們正在考慮對當前的特性集進行類似的擴展,如更高級的可觀測性功能,以捕捉每個模塊的業務相關指標,或可視化表述流經應用的事件 - 命令流。如果幾年後,我們能在儘可能多的 Spring Boot 應用中發現 Spring Modulith 構建的約定,不管它們遵循哪種架構風格,那就更好了。

該項目已經發布 0.1 版本。更多細節可以在文檔和 GitHub 上的源代碼中找到。

原文鏈接:

Spring Modulith Structures Spring Boot 3 Applications with Modules and Events(https://www.infoq.com/news/2022/11/spring-modulith-launch/)

聲明:本文為InfoQ翻譯,未經許可禁止轉載。

今日好文推薦

Serverless時代已經全面到來:冷啟動時間降低90%,數據分析All on Serverless

如何破解Web3的「存力」難題?

後Kubernetes時代的未來?Wasmer 3.0 發布,可在瀏覽器外運行 WebAssembly

馬斯克要求推特程序員寫周報,具體到代碼行數;劉強東稱將末位淘汰部分京東高管;閏秒終於要被取消了!| Q資訊

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()