領域驅動設計(DDD):領域接口化設計

**領域接口化設計
**

把服務對象(service)和資源庫對象(repository)設計成接口是最常見的。但是這對接口化的認識還遠遠不夠,我們需要更深入地去分析接口化設計和更全面地應用接口化編程。所以我們要討論的是全面接口化,尤其是對領域模型接口化的認識。

=============================================================================================================================

領域接口化

通常的情況下我們會把領域模型設計成類(class),但是你有沒有想過把領域模型設計成接口(interface)?比如:


這時候看起來有點東西,因爲我們爲了適配不同的數據源,提供了不同的實現類。

最開始要把領域對象設計成接口,確實是爲了在不同的 ORM 框架之間實現無縫切換。因爲 JPA 對面向對象的支持最好,而 Mybatis 因爲簡單在大環境下比較流行。在解決這個問題時,通常使用層內包裹或者叫對象轉換的方式來解決。具體來說是在持久層使用持久化對象(PO)與領域對象(DO)的之間進行轉換。例如:

補充 JpaUser.of() 方法的實現:

對於使用 JPA 或者 Elasticsearch 等等各種不同的數據源,Spring data 都爲此做了全面的支持。但由於 User 是接口,Spring data 提供的 Repository 接口泛型只支持具體類型,比如:

爲了解決這個問題,我們需要使用委託的方式,如下:

關聯接口化

接口之間的關聯關係依然需要具體到子類的關聯關係上來討論。

對於需要持久化的實體來說,我們不可能直接在成員屬性上使用接口類型,因爲持久化框架無法通過接口來判定具體實現類。如下:

對於泛化關聯關係問題,我們可以使用 JPA 註解提供的 targetEntity 屬性來解決:

對於不支持類似 targetEntity 屬性的框架或者其它持久化技術,我們可以使用封裝來解決。如下:

如果使用的是 Mybatis 作爲持久化框架,依然可以在 OrderMapper.xml 中進行配置來解決:

在解決掉不同數據源無縫切換和關聯關係特化的問題後,在創建 User 對象上就和以往使用 new 的方式有所不同了,如下:

再過去創建對象都是使用 new 關鍵字,然而現在要使用 UserService 提供的 createUser(String id) 來創建。

這種思維的轉變可能讓你初次不太很適應,但在考慮另一個問題。

系統接口化

對於一個產品我們要考慮的不只是產品本身能解決的業務需求,還需要在部署上有所追求。如果項目初期的併發量很小,客戶可能採用單進程的方式部署,慢慢地單進程扛不住了會升級到集羣的方式,最終還要升級到微服務的方式。如何在單進程、集羣和微服務之間進行無縫切換呢?


再過去單機和集羣項目與微服務項目是不能兼容的,因爲領域模型都是類(class)而不是接口(interface)。具體來說:服務提供者(provider)的 User 對象與服務消費者(Consumer)的 User 對象是不兼容,不兼容將導致在單機項目中使用的是服務提供方的內部 User 對象,而一旦遷移到微服務項目後,需要大量的修改工作。要把以前調用方使用內部 User 對象替換爲服務消費者提供的 User 對象。這樣的工作也是不可以逆的,一旦遷移成功就不能降級到單機環境了。

再過去我們確實把服務(service)設計成了接口,這種接口的設計對於內部的開發看似會有幫助,但是從實戰的經驗來看卻不像大家想象的那樣可以爲 Service 提供不同的實現。因爲現在都是迭代開發,都是一個版本一個版本的去不斷完善應用服務代碼,而不是替換應用服務代碼,所以在 IDDD 中把應用服務(Application Service)類型由接口(Interface)改爲了類(Class)。

如果我們把領域對象設計成接口類型,並與服務接口以及其它接口一起組織在一個新的模塊內,形成一個新的接口(API)模塊。然後爲各種不同地端口提供適配此端口的實現,這樣的設計是不是可以解決在運行環境中無縫切換的問題,如下:

這樣的設計使得調用者只需要使用 User 接口(user-api)開發業務,並且在單進程(Standalone)環境中只需要依賴 user 模塊,在微服務環境中只需要依賴 user-openfeign-client 模塊,在外部環境中只需要依賴 user-rest-client 模塊。調用者通過依賴不同地實現模塊來解決不同環境的無縫切換,並且調用者使用的代碼是不需要改變的。

開源電商

Mallfoundry 是一個完全開源的使用 Spring Boot 開發的多商戶電商平臺。它可以嵌入到已有的 Java 程序中,或者作爲服務器、集羣、雲中的服務運行。


項目地址:

https://gitee.com/mallfoundry/mall

總結

領域對象接口化使得我們在內部實現了一套統一的接口,並將領域對象接口化擴展到系統級別時,我們又在系統層次上設計出一套統一地全局接口來開發業務和應對未來變化的環境。這樣的設計雖然非常好,但對軟件設計人員、軟件架構師以及開發人員的專業性也有了一定的要求,但是它所帶來的好處是可見的。

作者:不夠具體

來源:https://juejin.cn/post/6894109393173315597

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/RhOSihdTjkdggtbpSXUeBA