前端架構演進 - 從單體到微前端(理論篇)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們首先需要認識到每一個系統的架構都不應該是一成不變的,爲了應對業務的變化,我們不應該只有重寫這一個選項。但往往架構的遷移業務方不會給開發人員預留充足的時間,在短時間內平滑地將舊的架構向新的架構演進就成爲了一個需要解決的問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文將從一個我最近經歷的項目出發,講解我們是如何在兩週時間內將一個單體前端應用演化爲一個","attrs":{}},{"type":"link","attrs":{"href":"https://teobler.com/posts/20201125-micro-frontends","title":"","type":null},"content":[{"type":"text","text":"微前端應用","attrs":{}}]},{"type":"text","text":"的。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"爲什麼有這次演進","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ce/ce16264583e2c9c2959c30a4216f82fe.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"不是爲了解決問題胡亂上莫名其妙的解決方案就是耍流氓","attrs":{}},{"type":"text","text":"。微前端和微服務一樣都是爲了解決問題而誕生的解決方案,先看看你的項目是不是也遇到了這些問題,再決定做不做吧。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我們項目是一個 To B 的交付項目,是某組織爲其多個部門協調合作爲願景而設想的一個系統。各個部門的工作人員爲了完成各自的業務需要訪問該系統下的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"一個或多個","attrs":{}},{"type":"text","text":"子系統。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這樣的業務場景下在項目開始之初後端很自然地選擇了微服務作爲業務解耦和降低系統複雜度的解決方案,但前端因爲考慮不周加上客戶比較保守並沒有採取微前端的解決方案,而是以分文件目錄的方式嘗試區分各個子系統。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在第一個子系統順利完成交付後我們意識到了一些問題:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一期項目上線後轉由公司內另外的組維護,我們在做後面的項目時難免會修改到一期或者公用的代碼,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"兩個團隊","attrs":{}},{"type":"text","text":"勢必會造成","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼衝突","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"整個系統過於龐大,我們的體量沒辦法喫下整個合同,可預見","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"未來會有第三方甚至第四方公司加入交付","attrs":{}},{"type":"text","text":",現在的單體應用不但會造成大量的團隊代碼衝突,而且","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"限定了整個項目的技術棧","attrs":{}},{"type":"text","text":",不利於後期的跨團隊合作","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然我們的應用通過 AWS EKS 部署,沒有宕機的煩惱,但是現在的架構","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"無法實現獨立部署","attrs":{}},{"type":"text","text":",每一次子系統的部署","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"需要對整個應用進行打包","attrs":{}},{"type":"text","text":",同時如果一個應用掛了,將會影響整個系統,微前端可以在這件事上做得更好","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"演進發生的時機","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a5/a56afc3a48a187450d25bb44be6950ee.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"架構需要發生改變往往是因爲開發人員發現當前的架構沒辦法應對業務的發展和變化,需要改變架構來適應新的業務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也有可能是當前業務已經複雜到一定程度,需要對架構做一些改變來對業務做一些解耦降低整個系統的複雜度,使系統更易維護。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而不管是什麼原因,在真正開始改變架構時都需要在交付的過程中花費額外的時間精力。但前面我們也提到過,往往業務方不會給足夠的時間來讓開發人員完成架構的演進。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"選擇一個恰當的時機","attrs":{}},{"type":"text","text":"也就成爲了一個重要的點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就我們的情況而言,時機在一期項目上線後,二期項目準備階段,於是我們在新項目的第 0 個迭代啓動了前端架構演進。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而如果我們就是連兩個周的時間都沒有,那麼就真的只能在交付的過程中加入一些 tech 卡,在別的分支上邊交付邊改進。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"目標","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/99/99514c24b5d2f6902277642c4f034b0f.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先既然是架構的演進,那麼就不會有完成的那一天,但是應該有一個","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最小的目標","attrs":{}},{"type":"text","text":",只要達成了這個最小的目標,已經能解決開發過程中的主要問題,這次演進就算是達到目的了,基於此我們在演進開始前規劃了相應目標:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"不動基礎設施,盡最大可能節省工作量,將所有應用打包後部署到同一個 nginx,將不同的應用放在不同的 folder 下,後續項目穩定後再推動客戶拆分基礎設施","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"先做最壞的打算,假如我們兩個星期內完不成拆分該怎麼辦","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"保持 master 代碼不動,計劃後續如何在 master 代碼分支上也能繼續開發,同時新建分支完成代碼拆分工作","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"保持現有 pipeline 不動,用於支持現有 master 分支代碼,新建一條全新的 pipeline 適配新的應用","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"先只拆分整個應用的代碼部分,時刻與 BA 和 後端小夥伴保持溝通,以業務形式和權限設計來指導前端如何拆分","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"技術選型","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9a/9abdef4a10325ff41fd5d1a467449f36.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個部分不一定每一次演進都會有,在我們的這個案例中,因爲我們需要將一個單體應用拆分成微前端,爲了減少拆分的工作量,增加項目的可維護性,我們需要挑選一個合適的微前端框架來解決這個問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說來也巧,在做出這個決定不久,公司發佈了","attrs":{}},{"type":"link","attrs":{"href":"https://insights.thoughtworks.cn/thoughtworks-techradar-vol-23/","title":"","type":null},"content":[{"type":"text","text":"第23期技術雷達","attrs":{}}]},{"type":"text","text":",我們關注到了 single-spa 做爲一個微前端框架已經進入到了”實驗“象限。同時進入我們視野的還有以 single-spa 爲基礎的 qiankun。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用 single-spa 完成一個小demo後我甚至都沒有了解 qiankun 就已經決定使用 single-spa 了,原因有以下幾點:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生態完備,官網的文檔很詳細,並且有社區和官方的一系列代碼庫例子,同時還有上傳在油管和B站的各種科普視頻","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"已經能解決我們想要解決的所有問題,並且從各個渠道搜索來看沒有致命缺陷","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尋求幫助響應極快,我在寫 demo 時遇到了一個沒法實現的需求,當晚我在官方 slack 提問,第二天一早就收到解決回覆","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"任務拆解","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/11/11d85378c1fb6224c5b791204cd86350.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"遇到了合適的時機,明確了需要達成的目標,完成了選型後要做的就是開動了,但是不得不再次提醒的是,我們需要做的是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"平滑演進","attrs":{}},{"type":"text","text":",所以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最重要的步驟","attrs":{}},{"type":"text","text":"其實是區分各類任務的優先級,通常我們會將任務劃分成以下幾類:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"必須要在短時間內完成的任務 - 這些任務如果不在這段時間內完成可能會 block 接下來的業務開發,可能會對未來的交付產生風險","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以晚點做的任務 - 這些任務不會 block 業務的開發,但是從業務和技術上來說都是應該完成的,而且越往後做這些任務所花費的資源將越多","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可做可不做的任務 - 這些任務往往是爲了提升開發體驗,不會直接影響整個應用或業務","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以不用做的任務 - 這些任務可以做,但是由於各種原因不在此次計劃中,可以推遲到未來時機成熟後完成","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在日常開發過程中,我們需要站在一個高位往前看,確認現在的架構是否能支撐未來的業務形式和變化,及時計劃架構調整和演進。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最重要的是,大多數架構的演進都是在時間不允許的情況下開始的,這時候我們需要對整個演進有一個計劃,對所有的任務排列優先級,先做最重要的那一部分,不重要的延遲決定,然後捨棄一些東西。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一件重要的事情是千萬不要在這個過程中自己給自己加戲,作爲開發人員,大家都想把每一個技術改進做到最好,但是給自己加戲的後果往往就是啥都想做好但是最後啥也沒做好。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"成功交付的前提是平滑演進。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章