搶佔先機
VMware 提供培訓和認證,助力您快速提升。
瞭解更多在設計軟體系統時,架構師和開發人員有很多架構選擇。微服務系統在過去幾年中變得無處不在。然而,單體式模組化系統的想法最近也重新流行起來。無論最終選擇何種架構風格,構成整體系統的各個應用程式都需要其結構具有可演進性,並能夠跟隨業務需求的變化。
傳統上,應用程式框架透過提供與技術概念(例如 Spring Framework 的原型註解,如 @Controller
、@Service
、@Repository
等)對齊的抽象來提供結構化指導。然而,將重點轉移到 使程式碼結構與領域對齊 已被證明可以構建結構更好的應用程式,這些應用程式最終更易於理解和維護。到目前為止,Spring 團隊就如何構建 Spring Boot 應用程式提供了口頭和書面指導。我們決定可以做得更多。
Spring Modulith 是一個全新的實驗性 Spring 專案,它支援開發人員在程式碼中表達這些邏輯應用程式模組,並構建結構良好、領域對齊的 Spring Boot 應用程式。
讓我們來看一個具體示例。假設我們需要開發一個電子商務應用程式,我們從兩個邏輯模組開始。一個訂單模組處理訂單,一個庫存模組跟蹤我們銷售的產品的庫存。本文的主要關注點是訂單完成後需要更新庫存的用例。我們的專案結構可能看起來像這樣(○
表示公共型別,-
表示私有型別)
□ Example
└─ □ src/main/java
├─ □ example
│ └─ ○ Application.java
│
├─ □ example.inventory
│ ├─ ○ InventoryManagement.java
│ └─ - InventoryInternal.java
│
├─ □ example.order
│ └─ ○ OrderManagement.java
└─ □ example.order.internal
└─ ○ OrderInternal.java
這種安排從通常的骨架開始,一個包含 Spring Boot 應用程式類的基本包。我們的兩個業務模組透過直接的子包反映出來:inventory
和 order
。庫存模組的安排相當簡單。它只包含一個包。因此,我們可以使用 Java 可見性修飾符來隱藏內部元件,防止其他模組中的程式碼(例如 InventoryInternal
)訪問它們,因為 Java 編譯器限制對非公共型別的訪問。
相反,order
包包含一個子包,該子包暴露了一個 Spring bean,在我們的例子中,它需要是公共的,因為 OrderManagement
引用了它。不幸的是,這種型別安排排除了編譯器作為防止非法訪問 OrderInternal
的助手,因為在純 Java 中,包不是層次結構的。子包不會隱藏在父包中。然而,Spring Modulith 建立了應用程式模組的概念,預設情況下,應用程式模組由 API 包(直接位於應用程式主包下的包,在我們的例子中是 inventory
和 order
)和可選的巢狀包(order.internal
)組成。巢狀包被認為是內部的,駐留在這些模組中的程式碼其他模組無法訪問。這個應用程式模組模型可以根據你的喜好進行調整,但在本文中,我們將採用這種預設安排。
為了驗證應用程式的結構以及我們的程式碼是否符合我們定義的結構,我們可以建立一個測試用例,該測試用例建立一個 ApplicationModules
例項
class ModularityTests {
@Test
void verifyModularity() {
ApplicationModules.of(Application.class).verify();
}
}
假設 InventoryManagement
引入了對 OrderInternal
的依賴,該測試將以下列錯誤訊息失敗,從而中斷構建
\- Module 'inventory' depends on non-exposed type ….internal.OrderInternal within module 'order'!
InventoryManagement declares constructor InventoryManagement(InventoryInternal, OrderInternal) in (InventoryManagement.java:0)
初始步驟 (ApplicationModules.of(…)
) 檢查應用程式結構,應用模組約定,並分析每個應用程式模組的哪些部分是其提供的介面。由於 OrderInternal
不在應用程式模組的 API 包中,因此 inventory
模組對它的引用被認為是無效的,因此在下一步(即呼叫 ….verify()
)中會報告此問題。
驗證以及應用程式模組模型的底層分析是透過使用 ArchUnit 實現的。它將拒絕應用程式模組之間的迴圈依賴,對被認為是內部型別的訪問(如上述定義),並且可選地,只允許引用透過在應用程式模組的 package-info.java
上使用 @ApplicationModule(allowedDependencies = …)
顯式允許列表的模組。有關如何在連結中定義應用程式模組邊界以及它們之間允許的依賴關係的更多資訊,請參閱參考文件。
能夠構建應用程式結構的模型對於整合測試也很有幫助。與 Spring Boot 的 slice test 註解類似,開發人員可以透過在整合測試上使用 Spring Modulith 的 @ApplicationModuleTest
來指示他們只希望包含特定應用程式模組的元件和配置。這有助於隔離整合測試,使其不受其他模組中測試更改和潛在失敗的影響。整合測試類大致如下所示
package example.order;
@ApplicationModuleTest
class OrderIntegrationTests {
// Test methods go here
}
與使用 @SpringBootTest
執行的測試用例類似,@ApplicationModuleTest
會找到使用 @SpringBootApplication
註解的應用程式主類。然後它會初始化應用程式模組模型,找到測試類所在的模組,並預設僅引導該模組。如果執行此類別並將 org.springframework.modulith.test
的日誌級別設定為 DEBUG
,您將看到如下輸出
… - Bootstrapping @ApplicationModuleTest for example.order in mode STANDALONE (class example.Application)…
…
… - ## example.order ##
… - > Logical name: order
… - > Base package: example.order
… - > Direct module dependencies: none
… - > Spring beans:
… - + ….OrderManagement
… - + ….internal.OrderInternal
…
… - Re-configuring auto-configuration and entity scan packages to: example.order.
測試執行會報告哪些模組被引導,其邏輯結構,以及它最終如何改變 Spring Boot 的引導過程以僅包含該模組的基礎包。它也可以進行調整,以顯式包含其他應用程式模組,或引導整個模組樹。
將整合測試的重點轉向應用程式模組通常會暴露它們的出站依賴項,這些依賴項通常透過引用駐留在其他模組中的 Spring bean 來建立。雖然可以使用 @MockBean
模擬這些依賴項以滿足測試執行,但通常更好的做法是將跨模組 bean 依賴項替換為釋出的應用程式事件,並由先前顯式呼叫的元件來消費該事件。
我們的示例已經按照這種首選方式進行了安排,因為它在呼叫 OrderManagement.complete(…)
期間釋出了一個 OrderCompleted
事件。Spring Modulith 的 PublishedEvents
抽象允許測試整合測試用例是否導致釋出了特定的應用程式事件
@ApplicationModuleTest
@RequiredArgsConstructor
class OrderIntegrationTests {
private final OrderManagement orders;
@Test
void publishesOrderCompletion(PublishedEvents events) {
var reference = new Order();
orders.complete(reference);
// Find all OrderCompleted events referring to our reference order
var matchingMapped = events.ofType(OrderCompleted.class)
.matchingMapped(OrderCompleted::getOrderId, reference.getId()::equals);
assertThat(matchingMapped).hasSize(1);
}
}
Spring Modulith 提供約定和 API 來宣告和驗證 Spring Boot 應用程式中的邏輯模組。除了上面描述的功能外,第一個版本還有更多功能可以幫助開發人員構建他們的應用程式
支援更高階的包結構安排。
支援靈活選擇一組應用程式模組以包含在整合測試執行中。
一個事務事件釋出日誌,允許開發人員在事務上下文中透過事件整合應用程式模組。
從應用程式模組結構派生開發人員文件,包括 C4 和 UML 元件圖以及應用程式模組畫布(每個模組的表格式高層描述)。
應用程式模組級別的執行時可觀測性。
您可以在其參考文件中找到有關該專案的更多資訊,並檢視示例專案。儘管已經提供了廣泛的功能集,但這僅僅是旅程的開始。我們期待您對該專案的反饋和功能想法。另外,請務必在 Twitter 上關注我們,獲取該專案的最新社交媒體動態。
Spring Modulith(沒有末尾的“s”)是 Moduliths(帶有末尾的“s”)專案 的延續,但使用了 Spring Boot 3.0、Framework 6、Java 17 和 JakartaEE 9 作為基礎版本。舊的 Moduliths 專案當前版本為 1.3,相容 Spring Boot 2.7,並將與其對應的 Boot 版本一起維護。我們在過去兩年中利用了從該專案獲得的經驗,精簡了一些抽象,調整了一些預設設定,並決定從更先進的基礎版本開始。有關如何遷移到 Spring Modulith 的更詳細指導,請參閱 Spring Modulith 的參考文件。