前后端一體化:前后端分離將死放前?

PS:本文所針對(duì)的場(chǎng)景,都是復(fù)雜業(yè)務(wù)場(chǎng)景下的 Web 應(yīng)用糯彬。簡(jiǎn)單的 Web 應(yīng)用不適合復(fù)雜的架構(gòu)模式凭语,它為帶來巨大的成本。

在接觸了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的概念撩扒,其中關(guān)于核心域的想法讓人頗為激動(dòng)叽粹。而在微服務(wù)架構(gòu)中,核心域是一個(gè)或者多個(gè)服務(wù)的域却舀,而位于核心域的核心則是領(lǐng)域模型。簡(jiǎn)單地來說锤灿,對(duì)于一個(gè)系統(tǒng)來說挽拔,它的核心的核心的 ”核心“ 就是:領(lǐng)域模型。

過去但校,當(dāng)我們談及領(lǐng)域模型的時(shí)候螃诅,我們往往將其置于 Web 后端的領(lǐng)域。當(dāng)我 GET 了一些基本的理念之后,便嘗試整合到前端架構(gòu)中术裸。去年倘是,我開始了 在項(xiàng)目上的第一次嘗試,并在 GitHub 上創(chuàng)建了 clean-frontend 項(xiàng)目袭艺,它以 Angular 作為示例搀崭,介紹了如何開發(fā)一個(gè)整潔前端架構(gòu)的前端應(yīng)用。

引子 1:模型一致化

我習(xí)慣于在公司的項(xiàng)目中引入成熟的框架猾编,從幾年前的 Angular.js 到近幾年的 Angular瘤睹,完整的框架能為企業(yè)帶來更少的維護(hù)成本,從而降低軟件開發(fā)成本答倡。幾年前轰传,我們引入了 Angular 之后,便開始大量地 TypeScript 項(xiàng)目實(shí)踐瘪撇。作為一個(gè)支持靜態(tài)類型的語言获茬,它非常適合于開發(fā)大型應(yīng)用、企業(yè)應(yīng)用倔既,在這一點(diǎn)上你可以從 React恕曲、Vue 等框架建議使用 TypeScript 看到一種趨勢(shì)。

采納了 TypeScript 之后叉存,當(dāng)前端從后端獲取到數(shù)據(jù)之后码俩,那么它需要將 JSON 數(shù)據(jù)映射到對(duì)應(yīng)的 interface。如此一來歼捏,前端的模型也就有了對(duì)應(yīng)的領(lǐng)域模型稿存。而這個(gè)數(shù)據(jù)模型和后端的數(shù)據(jù)模型應(yīng)該是完成一致的,但是實(shí)際上瞳秽,它往往是落后的瓣履。后端的 API 發(fā)生的變更之后,才需要前端同步去修改模型练俐。

而當(dāng)我作為一個(gè)前端的 Tech Lead 來考慮這個(gè)問題的時(shí)候袖迎,我首先想到的是:讀取后端的 Java 代碼,然后生成對(duì)應(yīng)的前端模型腺晾。在沒有造 Chapi 的輪子之前燕锥,通過 TypeScript Compiler 轉(zhuǎn)換獲得對(duì)應(yīng)的類型,是我覺得比較靠譜的方案悯蝉。在有了 Chapi 之后归形,我覺得它們都是小問題了。

不過鼻由,然后我一想這樣做的意義并不大暇榴,還不如:套用后端的契約測(cè)試厚棵,造一個(gè)前端的自動(dòng)化契約測(cè)試,即:mest蔼紧。它通過 API 返回?cái)?shù)據(jù)和 TypeScript 的 Interface 來完成對(duì)于契約的測(cè)試婆硬。一個(gè)簡(jiǎn)單的測(cè)試數(shù)據(jù)如下:

url,interface
https://phodal.github.io/mest-test/error.json,mock/IError.ts

通過 HTTP 請(qǐng)求獲取對(duì)應(yīng)的測(cè)試數(shù)據(jù),再將其與本地的模型進(jìn)行比較奸例。

不過呢彬犯,我們還要使用 JavaScript 語言重寫部分 Java 代碼,那么我們?yōu)槭裁床灰?JavaScript / TypeScript 重寫一切呢哩至?

引子 2:JavaScript / TypeScript 重寫一切

遺憾的是躏嚎,使用 JavaScript 編寫后端應(yīng)用(BFF、膠水層除外)菩貌,在大部分的大公司是比較難的卢佣。內(nèi)部的生態(tài)鏈和運(yùn)維影響了技術(shù)決策,使用 C++ 不香嗎箭阶,使用 JavaScript 會(huì)存在無人在背后支持虚茶。

純 Node.js 后端應(yīng)用

在過去的幾年里,我建議:小公司的后端應(yīng)用不要使用 JavaScript / TypeScript 編寫仇参,因?yàn)檫\(yùn)維嘹叫、監(jiān)控、APM 等生態(tài)尚不夠完善诈乒。小公司應(yīng)該優(yōu)先投資于業(yè)務(wù)領(lǐng)域罩扇,在基礎(chǔ)設(shè)施的投入見效比較短,除非能控制好開發(fā)人員的流動(dòng)性怕磨。

不過喂饥,不管怎樣,已經(jīng)有大量的小公司因?yàn)槿肆Τ杀镜脑虺辏呀?jīng)使用上了 Node.js 來開發(fā)后端應(yīng)用员帮。

Serverless 應(yīng)用

而隨著 JavaScript 系生態(tài)的完善,基礎(chǔ)設(shè)施已經(jīng)不是會(huì)成為小公司的負(fù)擔(dān)导饲,我便覺得這是一個(gè)好的時(shí)機(jī)捞高。不過,我指的是采用 Serverlesss 架構(gòu)渣锦,而非自建 JavaScript 系生態(tài)硝岗。Serverless 不僅幫助小企業(yè)解決了基礎(chǔ)設(shè)施的問題,還能為小企業(yè)降低軟件運(yùn)維成本袋毙。一旦企業(yè)做大之后辈讶,也可以自建采用 OpenFaas 等開源方案解決 Serverless 的供應(yīng)商鎖定問題。

不論是 Node.js 后端應(yīng)用還是 Serverless 應(yīng)用娄猫,因?yàn)槭褂玫氖峭环N語言贱除,我們可以輕松地在前后端之間共享代碼,可以是 Git submodule媳溺、NPM 包月幌、遠(yuǎn)程等等的方式。

引子 3:純編譯到 JavaScript

市面上有各種各樣支持 compile to js 的語言悬蔽、框架扯躺,諸如于 Kotlin.js、Scala.js蝎困、Python.js 等等录语。

對(duì)于小前端的應(yīng)用來說,這種架構(gòu)非常的不錯(cuò)禾乘。它相當(dāng)于是漸進(jìn)式的系統(tǒng)架構(gòu)方案澎埠,當(dāng)前采用了主流的前端框架,而非傳統(tǒng)的后端渲染機(jī)制始藕,并統(tǒng)一了技術(shù)棧蒲稳,降低了組織內(nèi)部的學(xué)習(xí)成本。不過伍派,它帶來額外的調(diào)試因素江耀,畢竟每多一層封裝,系統(tǒng)的復(fù)雜度就需要 * 2诉植。

而為了讓框架的使用者支持不同的框架 React祥国、Angular、Vue晾腔,這個(gè)框架還需要提供這些框架的 bind 或者是 wrapper舌稀,以提升框架使用者的幸福感。

但是建车,我們的挑戰(zhàn)依然是復(fù)雜的前端應(yīng)用扩借,以及它難以消除交互的復(fù)雜度。

引子 4:共享領(lǐng)域模型/模式庫(kù)

開始之前缤至,我不得不強(qiáng)調(diào)一潮罪,領(lǐng)域模型是一種包含數(shù)據(jù)和行為的抽血模型。

編譯到 JavaScript 是一種輕前端的方案领斥,而對(duì)于重前端的項(xiàng)目來說嫉到,它們完成可以采用一種新的模式:共享領(lǐng)域模型。即將領(lǐng)域模型作為模式庫(kù)月洛,提供給前后端一起使用何恶。即,我們只需要編譯所需要的部分嚼黔。

這在我使用了 Kotlin 的多平臺(tái)技術(shù)(multiplatform)重寫了 Chapi 的 domain 層之后细层,我意識(shí)到了這是一個(gè)非常迷人的方案惜辑。我即使用在前端代碼中使用 Chapi 的領(lǐng)域模型,我還需要在后端的代碼中使用這套模型疫赎。原先盛撑,我需要手動(dòng)翻譯一行行代碼,現(xiàn)在我并不需要這樣的一個(gè)步驟捧搞。只需要在 pom.xml抵卫、build.gradle 或者是 package.json 中引入依賴及其對(duì)應(yīng)的版本即可。

在引入了這部分的代碼之后胎撇,我們?cè)訇P(guān)注于 UI 交互部分即可介粘。

引子 5:領(lǐng)域模型編譯到 WASM

考慮到并非所有的語言都能支持 compile to JavaScript,一種頗為有效的方式就是使用 WASM晚树。

WebAssembly 或稱 wasm 是一個(gè)實(shí)驗(yàn)性的低端編程語言姻采,應(yīng)用于瀏覽器內(nèi)的客戶端。WebAssembly 將讓開發(fā)者能運(yùn)用自己熟悉的編程語言編譯题涨,再藉虛擬機(jī)引擎在瀏覽器內(nèi)運(yùn)行偎谁。 —— 維基百科

我嘗試將使用 Go 編寫的 Coca 編譯成 WASM,但是遇到一系列的問題(我已經(jīng)忘了)纲堵,體積似乎是個(gè)問題巡雨,所以我嘗試使用 Rust 去構(gòu)建另外一種可能性。Rust 官方提供了 Rust Webpack Template席函,因此我可以將其集成到我現(xiàn)有的前端應(yīng)用中铐望。只是呢,似乎還沒有人會(huì)使用 Rust 去編寫后端應(yīng)用茂附。

但是 WASM 提供了一種更友好的方式正蛙,即我們不需要重寫現(xiàn)有的代碼,而是只需要添加一些代碼营曼,便可以將現(xiàn)有的后端模型代碼提到給前端使用乒验。并且,與混淆后的 JavaScript 相比蒂阱,它看上去更加安全 —— 學(xué)習(xí)成本更高一些锻全。

引子 6:ComponentLess

在研究 Serverless 和微前端的期間,我突然有了一點(diǎn)想法录煤,我對(duì)于客戶端領(lǐng)域的 Serverless 式架構(gòu)有了一些基本的構(gòu)想鳄厌,叫:ComponentLess。盡管有了一些基礎(chǔ)的理念妈踊,但是還缺乏一個(gè)真實(shí)可用的 Demo了嚎,所以我并沒有定義出什么是 ComponentLess。

起先,我以為無代碼編程是一個(gè) ComponentLess 方向歪泳,但是一研究發(fā)現(xiàn)并不是萝勤。無代碼編程傾向于可視化編程,而 ComponentLess 傾向于使用 DSL 編程夹囚。就這一點(diǎn)來說纵刘,我便偏向于使用 Web Components + WAM 技術(shù)來構(gòu)建新的前端架構(gòu)。

ComponentLess

從單體應(yīng)用轉(zhuǎn)向微服務(wù)架構(gòu)的一大特質(zhì)是荸哟,組件(非單指 UI 組件,可以視為服務(wù))由函數(shù)調(diào)用轉(zhuǎn)向了 HTTP 調(diào)用瞬捕。而 Serverless 進(jìn)一步地將微服務(wù)的服務(wù)級(jí)別 HTTP 調(diào)用鞍历,細(xì)化為函數(shù)級(jí)別的 HTTP 調(diào)用。

對(duì)于前端領(lǐng)域來說肪虎,也是如此劣砍。微前端將單體應(yīng)用拆分一個(gè)個(gè)的獨(dú)立運(yùn)行前端應(yīng)用,我們可以隨意地組合這些應(yīng)用扇救。進(jìn)一步地刑枝,結(jié)合諸如于 Web Component 這樣的組件級(jí)方案,便可以將拆分細(xì)分為到 UI 組件的粒度迅腔。我們可以使用 (HTML Imports 已遺棄)script 標(biāo)簽從遠(yuǎn)程導(dǎo)入:

<script type="module" src="my-element.js"></script>
<my-element></my-element>

因此装畅,在未來,不論是前端開發(fā)人員沧烈,還是開發(fā)人員掠兄,都可以通過集成組件的方式來開發(fā)應(yīng)用。也就是說锌雀,我們只需要關(guān)注于編寫核心業(yè)務(wù)代碼即可蚂夕,剩下的部分可以通過一些特殊的方式來實(shí)現(xiàn)顶霞。

DSL 抽象化代碼

ComponentLess + Serverless 是代碼即基礎(chǔ)設(shè)施開始的一個(gè)標(biāo)志姐直。當(dāng)代碼開始作為基礎(chǔ)設(shè)施的一部分時(shí),代碼便需要以某種方式才能組合到一起谐檀。在 Serverless Framework 中惩歉,開發(fā)人員通過配置接入服務(wù)端所使用的基礎(chǔ)信息等脂。而 YAML 配置本身也是 DSL 的一種,缺乏靈活度柬泽,但是使用非常簡(jiǎn)單慎菲。

事實(shí)上,這是兩個(gè)選擇:

  • 配置 + 編程語言锨并。 上手容易露该,遷移難
  • DSL。 上手復(fù)雜第煮,易于遷移

但是解幼,無論如何我還是如何 DSL抑党,它聽上去有著更豐富的 KPI。至于如何抽象化基礎(chǔ)設(shè)計(jì)代碼撵摆,可以參考《云研發(fā):研發(fā)即代碼》一文底靠。

模型復(fù)用

不論是 ComponentLess 還是 Serverless,它們都是由函數(shù)特铝、UI 組件變?yōu)橐粋€(gè)獨(dú)立可運(yùn)行的單元暑中。為了與別人交互,它需要包含輸入和輸出鲫剿。而輸入和輸出本身是需要一個(gè)數(shù)據(jù)模型作為支撐的鳄逾,以此才能完成整個(gè)系統(tǒng)的穩(wěn)定性。

而這個(gè)話題已經(jīng)回到我們開頭所討論的內(nèi)容里灵莲。

前后端分離將死雕凹?

在所有的引子里, 我們已經(jīng)準(zhǔn)備了所有的論據(jù)政冻,所以只需要:

  • 使用可以跨前后端的語言枚抵,構(gòu)建領(lǐng)域模型
  • 將后端服務(wù)、前端設(shè)施細(xì)化為更小的組件
  • 設(shè)計(jì) DSL 將領(lǐng)域模型轉(zhuǎn)換到特定平臺(tái)的代碼

你就可以殺死前后端分離明场,就是這么簡(jiǎn)單汽摹。

前后端分離將死,不是現(xiàn)在榕堰,但是可能在五年后開始竖慧。

你說呢?

其它

架構(gòu)逆屡,沒有終點(diǎn)圾旨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市魏蔗,隨后出現(xiàn)的幾起案子砍的,更是在濱河造成了極大的恐慌,老刑警劉巖莺治,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件廓鞠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谣旁,警方通過查閱死者的電腦和手機(jī)床佳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榄审,“玉大人砌们,你說我怎么就攤上這事。” “怎么了浪感?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵昔头,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我影兽,道長(zhǎng)揭斧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任峻堰,我火速辦了婚禮讹开,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捐名。我一直安慰自己萧吠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布桐筏。 她就那樣靜靜地躺著,像睡著了一般拇砰。 火紅的嫁衣襯著肌膚如雪梅忌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天除破,我揣著相機(jī)與錄音牧氮,去河邊找鬼。 笑死瑰枫,一個(gè)胖子當(dāng)著我的面吹牛踱葛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播光坝,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼尸诽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了盯另?” 一聲冷哼從身側(cè)響起性含,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸳惯,沒想到半個(gè)月后商蕴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芝发,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年绪商,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辅鲸。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡格郁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情理张,我是刑警寧澤赫蛇,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站雾叭,受9級(jí)特大地震影響悟耘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜织狐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一暂幼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧移迫,春花似錦旺嬉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荡陷,卻和暖如春雨效,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背废赞。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工徽龟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唉地。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓据悔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親耘沼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子极颓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容