單體架構是一種軟體設計方法,其中應用程式的所有元件都整合為一個不可分割的單元。在這種架構中,整個應用程式(包括使用者介面、業務邏輯和資料訪問層)作為單一實體進行開發、部署和維護。
什麼是單體?
- 單一儲存庫 — 將原始碼共同定位在單個儲存庫中
- 單一應用程式 — 在單一應用程式中提供所有功能
- 單一服務——所有功能都存在於單一後端服務中
- 單一資料庫 — 所有資料庫表都存在於單一模式中
以下是一些關鍵投資,儘早考慮這些投資,以便做好準備,將未來的重構成本保持在最低水平。
- 工程思維——從前端到後端瞭解需求,但反向執行;從後到前構建。
- 領域驅動設計——從域和聚合之間專門構建的實體開始。
- 模組化 程式碼——在準備重構程式碼或遠端託管共享包時分離您的關注點。
- 資料庫表結構——每個限定上下文 有單獨的資料表結構。
- 解耦服務部署——定期確保您可以獨立部署每個元件。
不斷成長的團隊
創業起初,如果最小可行產品(MVP)獲得關注,那麼初創公司可能會獲得資金,並且將會出現增長期。毫無疑問,這意味著更多的人將加入團隊,並且需要分享最初工程師的願景。
早期,當團隊規模較小(2-15 人)時,流程通常比較鬆散,因為協調慣例的開銷是可控的。然而,隨著團隊規模擴大,一個團隊處理所有事情的效率會降低,因此會決定拆分團隊,組建更小、更靈活的團隊,分頭完成公司目標。
在任何公司生命週期的早期,都傾向於從整體結構開始,保持簡單(KISS 原則),然後透過提供最低限度(MVP )來保持每個決策的精益( YAGNI 思維方式) ;在此過程中降低質量。
然而,這是一種無知的錯覺,因為你的視野只能看到地平線,而你無法看到角落。需要一些遠見才能意識到即將發生的事情,並做好準備,積極應對未來的挑戰,否則將面臨一連串無休止的被動行為。
接下來的挑戰是,如果你不償還技術債務而讓複雜性增長的時間越長,你就會開始走向生產力斷崖式下降的境地。
在這個階段,需要投資的關鍵技術是針對原有的單體架構。如果你早點採取這些措施,那麼成本會更低;但如果你拖延,那麼債務就會隨著公司的發展速度成倍增長。
關鍵技術投資
- 遠端釋出——儘管短期內開銷會增加,但開始將共享程式碼釋出到遠端儲存庫。
- 解耦部署——將可獨立部署的後端服務重構到自己的儲存庫中。
- 組織設計——你現在有機會利用低成本主動設計團隊以獲得成功,否則你將因讓組織結構決定你將交付的軟體型別而受苦。(參考)
- 文件 — 花時間建立具有周到資訊架構的 wiki,開始構建寫作的組織能力。部落知識無法擴充套件,您將看到團隊凝聚力的喪失。
七種技術單體
初創公司遇到的七種最常見的技術整體模式。
經典單體
- (A) 單一程式碼庫
- (B)單體應用程式
- (C) 單體服務
- (D)單體資料庫
高階單體
- (E) 分散式單體架構
- (F)雲原生單體架構
- (G) GraphQL 單體
單一程式碼庫
第一種單體結構是大型的單一的 monorepo。如果有一個大型團隊在其中工作,那麼一個包含整個程式碼庫的單一倉庫最終將變得難以管理。
雖然在早期階段共置程式碼有很多好處,但最終會達到一個門檻,這些好處就會對你不利,因為有太多的問題混雜在一起。
- 耦合問題——如果沒有分散式思維,共享程式碼將位於同一位置,並且直接引用時缺乏版本控制,意味著任何依賴關係都會被強制升級。
- 治理挑戰——程式碼庫的表面積越大,治理活動就越慢。
- 實現多樣性——隨著工作負載的範圍和多樣性的擴大,將會出現各種變化,從而開始給共享空間帶來關注(想想從產品工程程式碼庫中出現的資料工程程式碼)
如果您從單一儲存庫開始,則沒有硬性規定何時應該進行轉換。您的答案將取決於您的具體情況,因為您總是會在降低未來生產力與近期優先事項之間進行權衡。
單體應用
大多數應用程式應該是可維護的。
可維護性定義為能夠在合理的時間內管理變更的功能,其中包括構建、打包、部署、自動化和手動測試。
當應用程式較小時,整個交付週期很快。然而,當您新增更多功能時,應用程式的範圍可能會從幾分鐘延長到幾個小時,在您意識到之前,您會在多個團隊之間進行高度協調的情況下一次等待數天。
因此,需要關注的指標是交付週期需要多長時間以及需要多少團隊參與。隨著交付週期的延長,團隊會因為變更數量過多而承擔更多風險,從而進行“大爆炸”釋出。這形成了一個良性迴圈,即部署時間膨脹和需要人力測試一切。
投資一項將應用程式拆分為可獨立部署單元的策略將允許團隊獨立工作和部署。在儘量減少不良使用者體驗並促進獨立部署的同時,制定一組粗略的功能。
單體服務
單體服務代表了一種場景,即後端 API 服務是在沒有領域驅動設計等基礎策略的情況下構建的。這可能會導致程式碼庫包含各種問題,並逐漸發展成為“泥球架構”。
由於邊界不明確,利用程式碼會受到系統穩定性問題的懲罰,從而逐漸導致速度變慢——改變系統某一部分的行為可能會在系統的另一部分引發無法預料的問題。
這使得未來的解耦工作變得更加困難,因為這些後端架構的基礎存在缺陷。我尋找的典型氣味包括以下內容。
- 具有形容詞欄位的實體
- 具有動詞欄位的實體
- 具有大量可疑欄位的實體
- 程式碼中缺乏層次
- 訪問模式不一致
不幸的是,這些挑戰本質上通常更具結構性,而且糾正這些挑戰的成本最高。您不僅需要重新繪製域,而且現在還必須重構程式碼並執行資料遷移。
建議是專門派一個團隊來盤點所有後端系統,優先考慮系統中損害業務運營或阻礙未來修復業務機會的部分。
- 部署WET 策略(將所有內容寫入兩次)以將您的功能提取到其自己的服務中。
- 使用Strangler Fig 模式重構Ball of Mud中的引用以使用新提取的服務。
- 透過刪除原始程式碼來完成DRY(不要重複自己) 。
值得注意的是,並非所有技術債務都值得償還。降低其餘工作的優先順序,只有在其上的工作才比其他工作更具商業價值。
單體式資料庫
最常見的出錯的單體模式之一是共享單體資料庫和單一巨型模式。
最早犯的捷徑是:讓員工自行決定關注點。然而,不可避免地,會有新員工或現有團隊成員必須做出決定,是按時完成任務還是遵守最初的關注點分離協議?這種反模式的問題包括潛在的安全性、隱私性和昂貴的重構。
單獨的憑證和表結構隔離是一種解決單體系統挑戰併為未來遷移到更模組化架構做準備的方法。
為應用程式的不同元件或功能模組建立單獨的資料庫憑證(如使用者名稱和密碼)。這樣可以實現:
- 訪問控制和安全性 - 透過授予特定的許可權和特權,只有授權的實體才能訪問和修改各自模組的資料。
- 職責分離 - 不同的團隊或元件只能訪問和管理自己的資料,促進更好的程式碼組織和可維護性。
透過在前期投入少量工作設定單獨的憑證和資料表隔離,可以為未來更容易地交付和測試單個元件,降低引入錯誤的風險,併為向模組化架構平滑過渡奠定模式。
雲原生單體式架構
在現代雲原生環境中,單個開發人員使用單個命令啟動整個技術堆疊從未如此簡單。這些一體化的奇蹟可以部署應用程式、服務及其所有必需的基礎設施。
挑戰在於它們被過度使用來代替傳統計算技術。結果是基礎設施泥團大團圓,將問題空間推給了基礎設施團隊。
當資料的依賴關係或流跳出一個上下文並建立與另一個上下文的緊密基礎設施耦合時,我們會遇到問題:
您開始看到新型分散式單體的出現:
- 它不僅基於軟體域耦合,
- 而且還基於雲基礎設施級別。
建議制定嚴格的規則,不要利用框架來獲取任何旨在利用或共享的核心元件。一旦出現這些要求,您就應該投資將程式碼與基礎架構分離。
GraphQL 聯合單體
這種聯合或聚合型別的單體架構是 GraphQL 所特有的,因為該技術具有遍歷整個 API 的能力。
當聯合發展到無法治理的地步時,挑戰就來了。隨著時間的推移,當您新增更多 API 時,您必須管理這些更改,以確保您沒有引入不希望與GraphQL 的其他部分結合的內容。
為了有效地進行治理,釋出到GraphQL 的每個人都必須審查所有內容 - 對組織造成高度的認知拖累。
建議如果希望利用聯合,請避免將所有內容都放入單一的超級圖中。相反,應圍繞每個聯合圖的目的劃定意圖邊界。這些邊界可以按應用程式、應用程式功能、安全隔離範圍等來劃定。
總結
天下沒有免費的午餐,尤其是當您的公司取得成功並擴大規模時。當複雜性增加時,您應該準備好重組你的單體系統,以便您的團隊不會因單體應用的慣性而陷入困境。
要點:
- 單體系統在初期技術創業公司中的作用和優勢(最佳化速度)
- 隨著公司發展和規模擴大,單體系統面臨的挑戰(技術債務、缺乏靈活性等)
設計系統的組織被限制於產生這些組織的通訊結構的副本的設計 — 梅爾文·E·康威
雖然康威定律 通常是在組織設計的框架內討論的,但我相信它是雙向的。系統的整體設計可能會在組織中造成通訊流和變革的瓶頸。