Alex Hudson在他的博客上表達(dá)了“軟件架構(gòu)正在走下坡路”的看法,以CQRS、事件溯源和本地存儲為例,說明軟件架構(gòu)存在的問題以及應(yīng)該如何正視及解決這些問題。以下內(nèi)容翻譯自作者博客,已獲得翻譯授權(quán),查看原文Software architecture is failing。
我懷疑軟件架構(gòu)在過去是否真的有過巔峰式的成功。Web三層架構(gòu)成為很多人進(jìn)入軟件開發(fā)領(lǐng)域的起點(diǎn),“12 Factor App”原則鼓勵開發(fā)人員采用能夠簡化開發(fā)和擴(kuò)展的最佳實(shí)踐。然而,在過去幾年,我發(fā)現(xiàn)那些提倡架構(gòu)的開發(fā)者們花了很大成本把高度復(fù)雜的系統(tǒng)強(qiáng)行塞到初創(chuàng)的項(xiàng)目當(dāng)中。在我看來,這樣只會讓事情變得更糟。
首先,我想在這篇文章里談?wù)撘恍┨囟ǖ哪J胶图夹g(shù)。我并不反對這些模式和技術(shù),我不覺得它們有什么問題,我只是覺得,它們當(dāng)中有一些在應(yīng)用方面存在一定的局限。我相信銀彈是不存在的,而且我認(rèn)為所謂好的架構(gòu)也僅限在它所要解決問題的領(lǐng)域,在進(jìn)行架構(gòu)設(shè)計時需要更多地考慮適應(yīng)性。
過去一年,我與多個技術(shù)團(tuán)隊(duì)及業(yè)務(wù)人員有過多次溝通,我總是聽到他們提到類似的問題。比如,“我們的交付速度不夠快”、“我們的系統(tǒng)太復(fù)雜,難以維護(hù)”、“我們?nèi)ツ杲桓兜南到y(tǒng)已經(jīng)過時了,但要替換它很困難”。
我看到很多技術(shù)團(tuán)隊(duì)在做出決策時并不知道如何將組織面臨的業(yè)務(wù)問題與技術(shù)戰(zhàn)略結(jié)合在一起。他們?nèi)狈夹g(shù)策略,通常會武斷地做出決定,比如“我們要百分之百地使用微服務(wù)”,但他們卻無法說清楚這一決定與實(shí)際業(yè)務(wù)之間有什么直接的聯(lián)系。
錯誤行為一覽
CQRS。最近我關(guān)注CQRS比較多,因?yàn)樗鼛缀鯚o處不在。有一天,我在Twitter上看到一篇關(guān)于“如何使用CQRS來開發(fā)MVP”的文章。對于大部分不了解CQRS的人來說,他們會認(rèn)為CQRS是一種RPC模式,可以用在復(fù)雜多變的數(shù)據(jù)存儲上,而且大部分入門教程都會說CQRS“適合用在大型復(fù)雜的系統(tǒng)上”。
很多文章說CQRS是解決微服務(wù)事務(wù)問題的良藥(但實(shí)際上并不是),但其實(shí)真正的原因是推崇CQRS的大部分開發(fā)者不喜歡ORM。
還有一種情況更加糟糕——代碼量大肆增加、系統(tǒng)的發(fā)布舉步維艱,但卻不能從中得到明顯的好處。
我看到有些系統(tǒng)正在使用CQRS,但實(shí)際上CRUD完全可以滿足他們的要求。于是我問他們?yōu)槭裁矗麄冋f是因?yàn)?ldquo;責(zé)任”或“領(lǐng)域問題”,但從來沒有人說其實(shí)他們是基于業(yè)務(wù)方面的原因而做出這樣的選擇。
事件溯源。事件溯源與CQRS是有關(guān)聯(lián)的,因?yàn)樗鼈兘?jīng)常被結(jié)合在一起使用。事件溯源的核心原理是說,你有一系列不可變的事件日志,使用這些日志可以創(chuàng)建出具有最終一致性的應(yīng)用視圖,而不是在RDBS里保存狀態(tài)。
這是一種典型的“不考慮業(yè)務(wù)需求”的技術(shù)選型。我見過兩個不同的初創(chuàng)公司,他們以“不可變?nèi)罩?rdquo;的形式保存客戶數(shù)據(jù)。如果你問他們“你打算怎樣做到GDPR的要求并將數(shù)據(jù)移除掉”,他們通常會說,“我們還沒想過這個問題”。我告訴他們,如果他們不處理好這些數(shù)據(jù)就會違規(guī),他們就面露難色。
本地存儲。有一個開發(fā)團(tuán)隊(duì)認(rèn)為前端都應(yīng)該做成單頁應(yīng)用,能夠處理離線內(nèi)容,這聽起來似乎很有道理。但問題是,他們處理的很多數(shù)據(jù)是敏感數(shù)據(jù),如果這么做,那些敏感的內(nèi)容就會散播到各種各樣的瀏覽器緩存里。
通常很少看到人們重用已有的技術(shù),反而是出于各種原因追求“熱門”的東西。看到那么多開發(fā)人員正在開發(fā)底層的東西,著實(shí)讓人吃驚。
我們做錯了什么?
我把這一切歸咎到技術(shù)領(lǐng)導(dǎo)者頭上,包括我在內(nèi)。那些沒有在大公司(比如Facebook、Google、Amazon)工作的技術(shù)人員,他們并沒有深入意識到在小型企業(yè)里應(yīng)該怎樣開發(fā)軟件系統(tǒng)。他們沒有深入討論怎樣做才算成功、怎樣做才是正確的以及如何總結(jié)出有用的模式。
很多技術(shù)文章大講特講如何構(gòu)建具有伸縮性、高性能的大規(guī)模系統(tǒng)。了解Netflix是如何面臨挑戰(zhàn)并解決問題絕對是件有趣的事情,了解Google在做出各種技術(shù)決策時是如何考慮問題對我們來說也大有裨益。但他們的大部分問題在其他公司并不會出現(xiàn),所以他們的解決方案也未必適合其他公司。
在當(dāng)今世界,我們很容易理解為什么有些團(tuán)隊(duì)癡迷于這些公司所說的解決方案。他們想快速發(fā)展,交付更健壯的服務(wù),不想落后。
我記得有一次跟一個工程師聊起他們的一個核心服務(wù)。他說,“它已經(jīng)徹底落伍了,沒有人維護(hù)它。只要不遇到極端情況,它都能正常運(yùn)行。要把它替換掉真的是太困難了,它表現(xiàn)得還算不錯,業(yè)務(wù)部門不想花時間去替換掉能夠正常運(yùn)行的系統(tǒng)”。這就是問題所在,他們把“成功”的定義(它表現(xiàn)得不錯,性能也不錯,我們沒必要把它替換掉)與“可用”的定義混為一談。
一項(xiàng)好的技術(shù)要能夠推動業(yè)務(wù)的發(fā)展,特別是對于技術(shù)公司來說。技術(shù)的核心部分是價值的來源,所以一定不能掉以輕心。很多軟件和服務(wù)就像是腳手架,我們可以利用它們來完成我們的工作。
我們應(yīng)該做些什么?
我們經(jīng)常無法決定是該開發(fā)軟件還是該購買軟件。開發(fā)軟件應(yīng)該是最后的選擇,“我們之所以要自己開發(fā),是因?yàn)槲覀冋也坏轿覀冃枰能浖?rdquo;。我想從更多的技術(shù)領(lǐng)導(dǎo)者那里看到他們是如何不通過開發(fā)軟件來解決問題的。
我想我們對未來擔(dān)憂得有點(diǎn)過頭了。雖然我們嘴上說“你根本不需要它”,但心里可不這么認(rèn)為。我們關(guān)注技術(shù)債務(wù),但同時又抱怨敏捷“不管用”。我希望看到更多的項(xiàng)目能夠把架構(gòu)決策延后到流程的后面階段。
我希望看到更多快速的交付。小型軟件看起來未必有趣,但只要能體現(xiàn)業(yè)務(wù)價值,那它就是英雄,而創(chuàng)建軟件的開發(fā)者也是真正的明星。如果我們能及早出發(fā),就能盡早到達(dá)目的地。我們需要重用已有的成果,要知道“混搭”并不是什么下三濫的手段。
我也特別希望能夠看到更多的開發(fā)者能夠在有約束的環(huán)境下工作。我經(jīng)常聽到有人說,“我們使用了某某產(chǎn)品或某某框架,它有一些無法解決的問題,所以我們自己推出了替代品”。或許這樣做是對的,但這并非唯一的解決辦法,要知道,條條大路通羅馬。
你所碰到的問題為你的發(fā)展奠定了基礎(chǔ),開發(fā)人員需要經(jīng)歷一些愚蠢的事情才能成長。我希望看到人們能夠看到事物的潛力,我們總是低估了現(xiàn)有系統(tǒng)的能力,同時又高估了自建軟件的能力。我想,是時候讓我們接地氣地關(guān)注中型企業(yè)的軟件架構(gòu)了。
其他人的評論
Alexey Zimarev
問題并非出在這些模式上,與架構(gòu)本身也沒有很大關(guān)系。架構(gòu)指的是高層次的管理、一系列非功能性需求以及教導(dǎo)和幫助他人的能力。CQRS和事件溯源并不是架構(gòu)。
從另一方面來看,開發(fā)者和架構(gòu)師的盲目崇拜才是問題所在。追求新技術(shù)的心態(tài)沒有止境,技術(shù)人員往簡歷上貼金的做法橫行于世。不過,正如Albero Brandolini所說的——如果開發(fā)人員沒有了業(yè)務(wù)方面的挑戰(zhàn),他們就會通過解決技術(shù)挑戰(zhàn)來獲得愉悅感。
開發(fā)者在一個領(lǐng)域?qū)W⒘颂L時間,開始從別處獲得“需求”,幾乎無暇顧及業(yè)務(wù)問題。最后,他們只能與技術(shù)為伴——新的框架、新的工具或者新的模式。
問題不在于架構(gòu),而在于業(yè)務(wù)與開發(fā)者之間如何看待對方。我希望能夠在各種開發(fā)者大會上呼吁這一觀點(diǎn),但他們似乎更熱衷于推銷各種“時髦”技術(shù)。
說到事件溯源和CQRS,我認(rèn)為關(guān)于“你不是Google”(請看之前的一篇熱門文章“一語點(diǎn)醒技術(shù)人:你不是Google”)的爭論有點(diǎn)偷換概念。即使你不是Google,也完全可以使用這些模式。如果你對領(lǐng)域知識知之甚少,反而能夠從使用事件模式中獲得好處,因?yàn)橥ㄟ^觀察過去發(fā)生的事件有助于你了解領(lǐng)域知識的來龍去脈。而在所謂的“CRUD足矣”的系統(tǒng)里,你只能看到當(dāng)前的狀態(tài),最終很可能會違反單一職責(zé)原則(SRP),并在數(shù)據(jù)庫層面出現(xiàn)大量的重復(fù)性事務(wù),這是一條不歸路(應(yīng)該說已經(jīng)變成了大泥球)。當(dāng)人們說“讓我們走回老路吧”,我就會感到很吃驚,就好像他們不知道他們過去構(gòu)建的系統(tǒng)已經(jīng)變成大泥球一樣。如果認(rèn)為這是因?yàn)殚_發(fā)者發(fā)生了變化,那就大錯特錯了。人是一樣的人,初衷都是好的,但對業(yè)務(wù)和系統(tǒng)的發(fā)展方向知之甚少,僅此而已。那么憑什么認(rèn)為現(xiàn)在就會比過去更好呢?
Niklas Lochschmidt
我已經(jīng)使用CQRS和事件溯源好幾年時間了。我非常贊同作者的一些觀點(diǎn),軟件不在于多,而在于精,自己開發(fā)框架也不應(yīng)該成為首選的方案。
不過我并不贊成“CQRS是一種RPC模式,可以用在復(fù)雜多變的數(shù)據(jù)存儲”這一說法。Martin Fowler在他的一篇文章中寫道:“CQRS的核心理念是說,你可以使用不同于讀取信息的模型來更新信息”。不過他也隨之給出了警告:“要知道,CQRS會增加大部分系統(tǒng)的復(fù)雜性”。RPC可以與CQRS一起用,但不是必需的。系統(tǒng)也可以一邊讀取文件,一邊對GraphQL查詢做出響應(yīng)。它的本質(zhì)是為命令(command)和查詢(query)構(gòu)建不同的模型。如果你不需要不同的模型,那么CQRS確實(shí)會帶來不必要的復(fù)雜性,但請不要混淆了一些概念(有些人認(rèn)為CQRS是一個框架,但其實(shí)它是一種架構(gòu)模式)。
我們使用事件溯源的目的不太一樣。我們需要核心系統(tǒng)全部的審計日志,并從日志事件流中分析出一些特定的度量指標(biāo)。事實(shí)上,我們經(jīng)常使用事件溯源,并基于此成功進(jìn)行過幾次系統(tǒng)重構(gòu),而純存粹的狀態(tài)驅(qū)動的系統(tǒng)里難以做到這點(diǎn)。捕捉到用戶的意圖和知道用戶行為的結(jié)果是兩碼事。也就是說,事件溯源對你的核心領(lǐng)域來說才是有意義的。另外,如果在一開始沒有考慮到GDPR(通用數(shù)據(jù)保護(hù)條例),后續(xù)就會有麻煩。在你有了數(shù)據(jù)庫備份或WAL(預(yù)寫式日志)歸檔之后,被要求清除敏感數(shù)據(jù)也是個問題,不是嗎?
Alex
我試著以一種簡單明了的方式為非技術(shù)人員解釋CQRS:在我看來,該模式里的命令部分更為重要,所以它才會與RPC相提并論。它與REST很不一樣,REST操作資源,而不是觸發(fā)命令。我知道其他的模式也存在問題,而作者以CQRS為例,可能是因?yàn)楹芏嗳俗罱荚趪L試使用CQRS(可惜都以失敗告終)。
我想另找時間分享我的一些有關(guān)CQRS和事件溯源的經(jīng)驗(yàn),我覺得它們應(yīng)該拿出來單獨(dú)講,而且很難講得好。這就好比討論函數(shù)編程語言和過程編程語言:LISP風(fēng)格的語言有很多好處,舒適的編程環(huán)境會帶來極高的效率。但并不是每個人都會這么覺得——我想知道為什么,畢竟這不是技術(shù)本身的問題。
我很贊同作者關(guān)于事件溯源的見解。我過去曾將它用在醫(yī)療記錄上:放射醫(yī)療報告就是事件溯源的一個很直觀的例子,雖然最開始它還處在理論階段。對基于時間的處理來說,它很顯然就是一個很好的方案。不過,我想很多開發(fā)者對最終一致性問題仍然心存疑惑。
關(guān)于GDPR的問題也值得另開話題。數(shù)據(jù)備份和WAL歸檔確實(shí)是個問題,但它并非一般性的問題,而且非常復(fù)雜。在系統(tǒng)里保留PII(個人識別信息)數(shù)據(jù)也是我比較關(guān)心的一個問題,盡管在軟件的角度加入了使用期限,但問題仍然存在。