Polylith:一款新的軟體架構
前言
Joakim Tendstrand是Clojure和Datomic的架構師和開發人員,最近向作者強烈推薦了自己團隊的產品ofollow,noindex" target="_blank">Polylith ,這個名字結合了“很多”和“石頭”的概念。它是一種軟體體系結構,利用許多構件來組合系統,所有構件就像工作在一個地方一樣。Polylith克服了單體、微服務、無服務架構的一些缺點,本文希望從概念和實現上對這個新架構做一個介紹,以引起開發使用者的興趣可以去採納並應用它。
Polylith的特點是功能簡單和可組合性。它可以幫助我們構建簡單、可維護、可測試和可擴充套件的系統。它像樂高(Lego™)一樣利用具有共享函式屬性的構件塊來構建系統。
將構件組合成系統:
介紹
當前架構是否可以改進?
下面對比了開發和生產體驗中的三個主流軟體架構:
- 單體(Monolith), 一種軟體體系結構,其中程式碼儲存在單個程式碼庫中並部署為單個人工製品。
- 微服務(Microservice), 一種由小型可獨立部署的服務組成的軟體體系結構,每個服務都在一個單獨的程序中執行,並通過網路與其他網路進行通訊。
- 無伺服器(Serverless), 一種基於雲端計算執行模型的軟體體系結構,雲供應商動態管理機器資源的分配。
開發體驗
生產體驗
這些體系結構提供了大量如何部署系統的指導,但對於如何在每個系統中構造程式碼指導很少。
- 現有軟體體系結構所經歷的偶然的、高度複雜性以及與開發相斥的挫折。
- 我們想要一個架構,它能給程式碼庫一個可理解、可維護、可重用和可測試的結構。我們想要一個一流的開發體驗,當我們作出改變時,能導航和重構所有的程式碼,並快速反饋。我們希望建立可伸縮的系統,但避免不必要的複雜性被“過早分發”。
- 我們想讓它能簡單、快速、有趣地構建任何大小的系統。
Polylith是什麼?
為了幫助解釋這個架構,Joakim和他的團隊發明了一種隱喻(metaphor) ,一個基於Clojure的體系結構,還演化了一個開發工具。
完整的Polylith體驗
- 隱喻幫助理解和交流系統。
- 體系結構將系統構造為高階的構件,並建立有效的開發環境來處理所有程式碼。
- Polylith工具使開發者能快速、有趣地與Polylith系統一起工作。
隱喻
介紹
隱喻有助於理解Polylith的建築概念。Polylith系統由三種類型的構件組成:元件 (Component)、基底 (Base)和庫 (Library)。每種型別的構件都表示打包程式碼的特定方法,其目標是使它們簡單地組合成系統。
基本知識
隱喻建立在兩個基本的功能概念上:函式和名稱空間。
功能
函式具有許多屬性,封裝性、簡單性、無狀態、純度。這些屬性使函式(尤其是純函式)具有牢固的、可組合和可測試的程式碼單元。這就是為什麼選擇在隱喻中使用類似樂高的磚塊,因為樂高就是一個牢固的、可組合的積木。
“乘法”乘以其自變數並返回結果
綠色的部位表示函式的實現
紅色的部位表示對另一個函式的依賴關係
底部的方形孔代表函式的簽名
簽名: 函式的名稱、引數和返回結果。
組合函式
讓我們來看一個更大的函式,以及函式呼叫在隱喻中的作用。
- "圓形區域"計算給定半徑圓的面積。
- 綠色部分表示函式的實現。
- 黃色部分代表了我們的“乘法”函式的依賴性。
- 底部的圓孔代表函式的簽名。
名稱空間
名稱空間打包了相關功能,作為一個共同術語並被賦予一個名稱。在Java中,這個概念被稱為包,在其它語言中,它被稱為模組。讓我們看看如何將函式打包到Clojure中的名稱空間中:
元件
元件是Polylith的核心構件,將系統劃分為封裝的和可組合的程式碼塊。通過將它們的實現與它們的介面分離來實現它們的封裝和可組合性。
淺綠色層是介面,深綠色層是實現
設計良好的元件具有單一職責、描述性名稱,並且公開了一組僅與其職責相關的清晰功能。舉幾個例子,一個元件可以:
- 是某領域實體,如使用者、帳戶或發票
- 管理與外部系統的整合
- 處理特定的基礎設施,如電子郵件、日誌記錄或儲存
基底
基底是Polylith的基礎構件,是一個系統與外界溝通的門戶。基底通過將實現與介面分離來實現封裝。
淺藍色層是介面(API),深藍色層是實現
每一個基底都有一個責任:向外界展示一個系統的功能。在Polylith中,可以在基底中自由地使用任何API技術,例如REST、RPC/">gRPC、GraphQL或只是簡單的舊的命令列。
基底和元件之間的區別是基座具有平的底部。這意味著基底是不可合成的,它們不能被其他構建塊所依賴。基底總是位於Polylith的底部。
庫
庫(Library)是Polylith的“屋頂”構件,是別人的程式碼,內部程式碼依賴於這些程式碼。
系統
Polylith的構件組合方法使得構建系統既簡單又有趣。一旦設計好了基底的API,那麼只需要組成一組正確的元件和庫,這些元件和庫就能實現了我們公開的功能。讓我們來看看一個完整的系統:
系統:一個基底 ,兩個元件,三個庫
生態系統
生態系統(ecosystem)是Polylith的擴充套件解決方案。它們是一個系統的集合,為外部世界提供一套統一的功能。在Polylith中,它們是通過一個“電纜”連線的,它從系統B中的一個元件的外部依賴性擴充套件到系統A的一個端點。
系統B連線到系統A的API提供的端點
架構
Polylith架構的設計主要圍繞兩個目標:簡單性 和速度 。簡單性來自於保持概念的獨立性,速度來自於把我們所有的程式碼放在一個地方。這一節介紹Polylith是如何利用Clojure來實現這兩個目標的,並用一個RealWorld的示例程式來描述(程式碼參考文章末連結)。
工作空間
工作空間“workspace”是Polylith專案中的根資料夾,這是使用元件組裝系統的地方。
可以把工作空間想象成一個辦公桌:一個與我們的積木一起工作的地方。它用“抽屜”來儲存構件,用“架子”來組裝系統,還有一個“工作臺”作為開發環境。
-
工作空間就像一個有抽屜、架子和工作臺的書桌:
- “抽屜”是我們儲存基底、元件和介面的資料夾。
- “架子”是我們配置和儲存系統的資料夾。
- “工作臺”是我們保持開發環境的資料夾。
基底
基底“bases”資料夾是一個“抽屜”,它儲存了工作空間中的所有基底。
每個基底都有一個標準的Clojure專案結構:使用Polylith.clj檔案(類似於pom.xml,被用於Maven和Java),一個readme.md檔案,一個資原始檔夾,一個src資料夾,和一個測試資料夾。每個基底都是一個專案,可以將它們編譯為單個的工件,這保證了它們與所有其他構件解耦。
“api”名稱空間公開了所有的基底
元件
元件“components”資料夾是另外一個“抽屜”,它儲存了工作區中的所有元件。
RealWorld元件“抽屜”包含八個元件
環境
環境“environments”是儲存所有環境的資料夾。每個環境都是一個可以在IDE或程式碼編輯器中開啟的專案。我們可以將環境資料夾想象成一個“工作臺”,它可以訪問工作空間中的所有環境。
環境是與構件一起工作的地方
這給了我們選擇多個環境的條件,每個環境都有不同的元件和基底。
介面
介面“interfaces”是儲存所有“工作空間介面”的資料夾。“工作空間介面”保證構件的實現互不訪問,實現元件之間的隔離。
系統
我們可以把系統“systems”資料夾想象成一組“架子”,它把工作區中的所有系統都儲存起來。
系統將選定的構件組裝成整個工件,通過構建和部署多個工件,從單體開發(monolithic在一個環境中擁有所有構件)出發,實現水平可伸縮性的提升。
工具
Polylith工具優化Polylith系統的產生、發展、測試和建造。Polylith工具幫助使用者:
- 建立工作空間、環境、系統、基礎、元件和介面的結構
- 僅僅測試或構建上次成功後改變的構件
- 檢視任何程式碼改變的狀況
學習Polylith工具的GitHub庫 ,瞭解它是如何工作的以及如何使用它。如果想快速瀏覽這個工具的所有特點,從命令部分 開始是一個很好的選擇。
總結
Polylith的優點
讓我們把Polylith與之前看的三種結構進行比較。Polylith系統可以作為單個工件(如單體)、作為生態系統中協作的多個工件(如微服務)部署,或者作為無伺服器體系結構中的Lambda功能。
開發
Polylith的單一開發環境允許我們在一個地方與所有的構建塊一起工作。這將我們的開發經驗與我們所選擇的部署架構斷開連線。
優點 | 解釋 |
除錯 | Polylith開發環境中的所有程式碼都可以在單個REPL 中執行,提供了一流的REPL驅動的開發和除錯體驗。 |
快速反饋 | Polylith工具跟蹤自上次成功測試或構建以來哪些基礎和元件已經被更改,並且只進行編譯和測試。這給我們在本地開發環境以及持續整合環境中構建和部署提供了閃電般的快速反饋。 |
重構 | 元件介面通過簡單函式呼叫連線到它們的實現。這意味著可以用IDE/程式碼編輯器安全地重構介面。 |
可重用 | 元件是可重用的,因為它們是封裝的,無狀態且可組合的。它們可以在單個系統和多個系統中重複使用。 |
簡潔性 | Polylith構件只是程式碼,是介面後面的函式集合。介面保證封裝,這確保了程式碼庫分離,保持更簡單的系統。 |
易測性 | 元件的封裝和功能性質使得它們作為完整的系統,易於被隔離測試 |
生產
Polylith不僅允許延遲部署的決策,還允許我們在需要時輕鬆地改變部署的架構。這是因為Polylith可以將元件重組成任意數量的系統,這樣就可以很容易地部署它們以滿足效能需求。
優點 | 解釋 |
成本 | Polylith允許我們避免對解決方案的分佈做出過早的設計決定。這使我們避免了“過早分發”,降低了部署複雜度並降低了運營成本。 |
部署 | Polylith工具使得構建和部署過程在本地和在我們的CI(持續整合)環境中都簡單且無縫。 |
可伸縮性 | 當Polylith系統沒有達到需要的效能/可伸縮性時,那麼Polylith使得建立新系統和水平伸縮變得容易。可以重用每個新系統中的現有元件。 |
實際應用
六個實際應用中的Polylith,所有服務的真實使用者
這些專案的規模和複雜度,從一個完整的招聘平臺到一個簡單的Web應用程式。這些專案告訴我們,元件是一個強大的工具,可以完成複雜的、任何規模的系統。構建像樂高一樣的系統比我們能想象的更快、更容易、更愉快!
遷移到Polylith
從一個單體遷移到Polylith,微服務或無伺服器架構是相對容易的。這是因為我們可以單獨地將每個人工製品遷移到Polylith系統,而不需要改變我們的部署。
從單體架構
不要忘記檢查系統編譯、構建和所有測試在每個步驟之後通過。
- 建立一個新的工作區,並新增一個新的系統,帶有一個空的基底。
- 將整塊程式碼,包括API和庫複製到這個基底中。
- 從基底(除了API)中提取所有程式碼為單個“monolith”元件。
- 重構程式碼來提高系統的質量。從我們的“monolith”元件一次一個元件開始:
一個有五個組成部分的系統
從微服務架構
微服務是一個由許多小單體組成的體系結構。這意味著遷移到Polylith就像在每個服務上執行單體遷移步驟 一樣簡單:
從無服務架構
無伺服器是一個由許多Lambda函式組成的體系結構。這意味著遷移到Polylith就像在每個Lambda上執行單體遷移步驟 一樣簡單:
具有單個端點的Lambda函式
下一步
如果你想了解更多關於Polylith:
- 如果您已經閱讀了所有的文件,但是還沒有掌握所有的概念,那麼我們建議您觀看視訊 。
- 如果您通過檢視程式碼學習得最好,然後轉向RealWorld示例應用程式 。可以克隆這個專案並開始探索,或者只是瀏覽程式碼並閱讀它的設計。
- 如果您想討論Polylith,那麼來和我們一起在Polylith論壇 上聊天。使用它作為一個空間來打招呼,給出反饋,問問題,分享您的Polylith經驗.
Polylith團隊
團隊成員 | 角色 | 聯絡 |
Joakim Tengstrand | “Polylith之父”。發明了Polylith體系結構,開發了第一個Polylith系統,開發了Leiningen外掛,共同編寫了簡報和本GitBook文件。 |
@jtengstrand |
Furkan Bayraktar | 共同開發了第一個Polylith系統,開發了5個生產Polylith系統,共同開發了Leiningen外掛,開發了RealWorld示例應用程式。 |
@furkan3ayraktar |
James Trunk | 領導了Polylith團隊,發明了隱喻,編寫了簡報和GitBook文件。 |
感謝張嬋對本文的審校。