使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

banq發表於2019-09-23

在我工作過的一家公司中,我的團隊被要求將舊應用程式移植到全新的堆疊上(例如從EAR / SQL應用程式遷移到獨立的/ NoSQL應用程式)。通過研究,我們很快意識到我們必須重做整個基礎架構……新框架與十年前所使用的框架有很大不同。實際上,唯一不需要更改的就是業務邏輯。因此重用它是有意義的,對吧?

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

經過更深入的研究,名為model的Maven模組只是POJO,完全是貧血的 …儘管還有服務模組,但是業務邏輯在所有層之間都是共享的,淹沒在許多技術程式碼(DAO建立,序列化,池管理)中等)。業務的某些部分依賴於我們嘗試刪除的舊框架的技術。在技術程式碼和業務邏輯之間沒有明確的區分。

關注點分離

我們尋求一種方法來實現這種分離,即如果有一天我們必須再次更改技術堆疊,則可以完全重用業務邏輯。我們的一位同事向我們介紹了六角形架構。該體系結構的關鍵概念之一是將所有業務模型/邏輯放在一個地方。因此,如果我回顧以前的架構,我們將得到以下內容:

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

另一個關鍵概念是領域只依賴於自身 ; 這是確保業務邏輯與技術層分離的唯一方法。我們如何在先前的架構上實現這一目標?領域顯然取決於持久層!

通過使用一種模式:控制反轉IOC。如果我們以此來重做以前的模式,並將領域稱為Hexagon,因為Alistair Cockburn(HexArch的建立者)喜歡這種六邊形形狀:

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

好的,控制反轉非常神奇,稍後我們將看到其工作原理。但是現在您瞭解了什麼是六角結構:

  • 只有兩個世界,在六邊裡面是所有 的業務模式/邏輯;六邊外:是基礎設施,代表你的所有技術規範。
  • 依賴關係始終從外部進入內部,這確保了業務域的隔離(因此,如果您以後更改基礎架構,業務邏輯可以重用!)。
  • 一個必然的結果是,六邊形內一切一定不能依賴任何技術框架,包括諸如Jackson或JPA之類的外部註釋。要確保使用maven,可以使用執行器外掛

讓我為您解釋最後一點,這是非常重要的一點。在另一個經驗中,我的團隊不得不將應用程式從“傳統” Spring框架移植到Spring Boot。Spring Boot為您簡化了很多事情;這意味著它有時可能與Spring框架有很大的不同。我們遇到的主要(痛苦)問題是,我們在Spring Integration Tests中大量利用其來驗證我們的功能,而這些功能在很大程度上依賴於Spring。而且因為我們沒有意識到Spring Boot與我們正在使用的其他框架有特殊的整合,所以我們第一次整合它時,所有這些測試都失敗了。我們無法說出我們是在某個地方破壞了業務邏輯還是源於純粹的技術問題。

通過確保六邊形不依賴任何框架,即使您決定更改堆疊,也可以確保您的業​​務域可以重用。您還將增加域的可測試性,因為您不再將其與整合問題混在一起,因此您將進行實際的功能測試。這樣,這些測試將直接與六邊形互動,並且僅與六邊形互動。我稍後再討論。

六邊形魔術

還記得控制權的倒置嗎?為了確保六邊形的隔離,對下游層的依賴性已被反轉。如您所見,該技巧實際上非常簡單:

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

六邊形(基礎結構)的外部分為兩個虛擬部分,左側和右側。在左側,您可以查詢域的所有內容(控制器,REST層等),在右側,您可以為域提供一些資訊/服務的所有內容(持久層,第三方服務等) )。為了讓外部與域進行互動,Hexagon提供了分為兩類的業務介面:

  • API收集所有查詢域所需的所有介面。這些介面由六邊形實現。
  • SPI(服務提供者介面)收集所有由領域檢索資訊或從第三方獲得某些服務所需的介面。這些介面在六邊形中定義,並在基礎結構的右側實現。我們將看到,在某些情況下,六邊形也可以實現SPI。

這裡有兩個重要事實:

  • API和SPI是六邊形的一部分。
  • API和SPI僅操作六邊形的域物件,以確保隔離。

在經典的分層架構中,業務物件或服務通常會建立DAO實現持久層。在六邊形中,域僅處理域物件。持久層負責將域物件轉換為要持久化的任何技術物件(通過DAO),如帶註釋的JPA POJO或其他方式)。

你能感覺到力量嗎?

六邊形體系結構也稱為埠和介面卡體系結構。它來自於此體系結構的模組化功能。因為一切都是分離的,所以您可以在領域的前面同時擁有一個SOAP,REST和JMS層,而不會對其產生任何影響。在SPI方面,如果需要,您可以從MongoDB驅動程式實現更改為Cassandra。由於更改永續性模組不會更改SPI,因此其餘軟體不會受到影響。API和SPI是埠,使用或實現它們的基礎結構模組是介面卡。

如何執行呢?

這裡還有一個規則:總是從六邊形的內部開始。這將為您帶來很多好處:

  • 關注功能而不是技術細節。因為只有功能才能為您的公司帶來價值。在另一個業務領域工作的開發人員可以放置一個Spring Controller。但是,除非他在一家會計公司工作,否則雙倍餘額遞減法對他來說聽起來就像Wookiee一樣。
  • 延遲技術實施的選擇。有時很難知道您真正需要哪種技術實現(例如Spring Data,JPA或Mongo,Cassandra,Redis等)。延遲此選擇可以幫助您專注於為公司帶來主要價值的要素-功能。此外,在實現了業務邏輯之後,一些新元素可以幫助您做出有關基礎結構的最佳選擇(例如,讀取多於寫入,或者域比預期多相關)。
  • 一個必然的結論是,它確保Hexagon是獨立的,我已經在隔離方面談論了很多,現在這很簡單。由於您切勿在沒有測試的情況下編寫程式碼,這意味著Hexagon是經過自我測試的。此外,我們在這裡獲得了僅針對業務的真實功能/驗收測試。

我的建議是首先以BDD / ATDD方式編寫您的功能/驗收方案。然後編寫API介面,它將成為功能的入口點。實施您的測試(贏取TDD!)以及實施您的業務邏輯之後。您可能需要定義一個SPI(例如從資料庫中檢索一些資料)就可以了。並且由於尚未實現右側,因此請在六邊形內部建立SPI的存根實現(例如,使用HashMap的記憶體資料庫)。

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

您可以選擇將存根實現保留在應用程式的測試範圍內,也可以根據需要臨時提供。例如,一旦我們在六邊形上實現了第一個功能,就需要對外部第三方服務和資料庫進行存根。因為我們的客戶需要我們提供介面合同,所以我們接下來通過REST控制器匯出了域。因此,我們在基礎架構的右側釋出了帶有存根資料的第一個版本,但是客戶端能夠看到我們的資料結構以及該功能的預期行為。而且,與手動建立功能的輸入和輸出的一些JSON示例相比,它要可靠得多,因為它實際上處理了實際的業務約束。

下一步通常是先開啟左側。這樣,您可以對功能進行一些整合測試。目前,您可以提供一些實時文件,並確保與客戶的介面合同。

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

最後,通過利用您進行的整合測試來實現功能的SPI,在右側開啟。我強烈建議您的測試是獨立的,以避免在構建期間出現任何不穩定情況。您應該始終使用Wiremock等外部服務或Fongo模擬MongoDB來模擬第三方。

使用六邊形架構解耦技術程式碼與業務邏輯 - Julien Topçu

為您的其他功能迴圈同樣的方法。

有關更多資訊,可在GitLab上獲取六角結構測試策略

總結的六邊形架構

將業務邏輯與技術程式碼脫鉤確實有好處。就技術的不斷髮展而言,它確保您的業務領域持久耐用。

六邊形形體系結構為您提供了一種實現此目標的真正方法:

  • 將所有業務模型/邏輯放在一個地方。
  • 域(六邊形的內部)是隔離的,並且與技術部分(六邊形外部的基礎結構)無關,因為它僅依賴於自身。這就是為什麼依賴項總是從六邊形的外部傳播到內部的原因。
  • 六邊形是一個獨立的模組。通過編寫無需處理技術問題的實際功能測試,它可以提高您域的可測試性。
  • 該體系結構提供了強大的模組化功能,可幫助您編寫所需數量的介面卡,而對其餘軟體的影響很小。而且由於該域在堆疊中是不可知的,因此可以在不影響業務的情況下更改堆疊。
  • 通過始終從六邊形的內部開始,您可以通過專注於功能開發來確保快速為公司創造價值。這樣,您可以延遲技術實施的選擇,以便在正確的時間做出最佳選擇。

六邊形結構不應在所有情況下使用。就像DDD(並且六邊形與之非常匹配)一樣,如果您擁有真正的業務領域,則這確實適用。對於將資料轉換為另一種格式的應用程式來說,這可能是過大的選擇。

為此,在採用新技術時始終要務實。如前所述,六邊形一定不能依賴任何技術框架,但是您可以例外。例如,在我們的案例中,六邊形有三個例外:Apache Commons Lang3(StringUtils),SLF4J和Findbugs的JSR305。因為我們不想再製造輪子,所以我們認為那些框架對領域的影響很小。六邊形的一個很好的副作用是您在整合新框架之前一直挑戰自己。在六邊形之前,我們已經獲得了該域的大約五十個依賴關係,並且已將其減少到其中的三個或四個。從安全形度來看,這是非常好的。

想看一些程式碼嗎?在GitLab上檢出使用六角結構構建的Kotlin / SpringBoot演示應用程式。

相關文章