自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

理想的應(yīng)用框架

開發(fā) 架構(gòu)
在過去對框架的設(shè)計中,我收到過的最有用的建議是:“不要一開始就根據(jù)現(xiàn)有的技術(shù)去整合和改進(jìn)。而是先搞清楚你覺得最理想的框架應(yīng)該是怎樣的,再根據(jù)現(xiàn)在的技術(shù)去評估,的確實(shí)現(xiàn)不了時再妥協(xié)。這樣才能做出真正有意義的框架。”

在過去對框架的設(shè)計中,我收到過的最有用的建議是:“不要一開始就根據(jù)現(xiàn)有的技術(shù)去整合和改進(jìn)。而是先搞清楚你覺得最理想的框架應(yīng)該是怎樣的,再根據(jù)現(xiàn)在的技術(shù)去評估,的確實(shí)現(xiàn)不了時再妥協(xié)。這樣才能做出真正有意義的框架。”

在這篇文章里,就讓我們按照這樣一條建議來探索一下現(xiàn)在的 web 框架最終可以進(jìn)化成的樣子,你絕對會被驚艷到。

前端,還是從前端說起。前端目前的現(xiàn)狀是,隨著早期的 Backbone,近期的 Angular、React 等框架的興起,前端在 模塊化、組件化 兩個方向上已經(jīng)形成了一定的行業(yè)共識。在此基礎(chǔ)上,React 的 FLUX、Relay 則是進(jìn)一步的對前端應(yīng)用架構(gòu)的探索。這些技術(shù)在目前國內(nèi)的大公司、大團(tuán)隊內(nèi)部實(shí)際上都落地得非常好,因為很容易和公司內(nèi)部已有的后端技術(shù)棧結(jié)合。而且這些 純前端框架的配套技術(shù)方案一般比較成熟,例如在支付寶確定使用 React,其實(shí)有一部分原因是它兼容 IE8,并且有服務(wù)器端渲染方案來加速首屏。

相比之下,像 Meteor 這類從前到后包辦的框架就較難落地。雖然能極大地提高開發(fā)效率,整體架構(gòu)非常先進(jìn),但架構(gòu)的每一個層級往往不容易達(dá)到行業(yè)內(nèi)的***標(biāo)準(zhǔn)。特別是在服務(wù)器 端,對大公司來說,通常都有適合自己業(yè)務(wù)的服務(wù)器集群、數(shù)據(jù)庫方案,并且經(jīng)受過考驗。因此當(dāng)一個團(tuán)隊一上手就要做面向十萬級、甚至***用戶的產(chǎn)品時,是 不太愿意冒風(fēng)險去嘗試的。反而是個人開發(fā)者、創(chuàng)業(yè)型的團(tuán)隊會愿意去用,因為確實(shí)能在短時間內(nèi)高效地開發(fā)出可用的產(chǎn)品出來。包括像 Leancloud 提出的這類型的服務(wù),也是非常受歡迎的。

這種現(xiàn)狀,就是理想和現(xiàn)實(shí)的一個爭論。Meteor 的方式能滿足我對開發(fā)效率的理想,而團(tuán)隊已有的技術(shù)方案能保障穩(wěn)定。能否整合其中的優(yōu)勢,不妨讓我們進(jìn)一步來細(xì)化一下對框架的希望:

- 有強(qiáng)大的前后端一致的數(shù)據(jù)模型層

  - 代碼可以可以復(fù)用。例如我有一個 User 模型,當(dāng)我創(chuàng)建一個新的 user 時,user 上的字段驗證等方法是前后端通用的,由框架自動幫我區(qū)別前后端環(huán)境。

  - 數(shù)據(jù)模型和前端框架沒有耦合,但可以輕松結(jié)合。這樣在前端渲染型的框架進(jìn)一步升級時,不影響我的業(yè)務(wù)邏輯代碼。

  - 由數(shù)據(jù)模型層提供自動的數(shù)據(jù)更新機(jī)制。例如我在前端要獲取 id 為 1 的用戶,并且如果服務(wù)器端數(shù)據(jù)有更新的話,就自動幫我更新,不需要我自己去實(shí)現(xiàn)輪詢。我希望的代碼寫法是:

  1. var user = new User({id:1}); 
  2. user.pull(); user.watch();  

實(shí)際上,Meteor已經(jīng)能實(shí)現(xiàn)絕大部分上述功能。但這不是軟文。我要強(qiáng)調(diào)兩點(diǎn)我不希望的:

  - 我不希望這個數(shù)據(jù)模型層去包含業(yè)務(wù)邏輯,也就是我創(chuàng)建的user對象,我不希望它提供 login、logout 等 api。

  - 我也不希望數(shù)據(jù)模型層自動和任何ORM框架綁定,提供任何 SQL 或 NoSQL 的數(shù)據(jù)支持。

看到這兩點(diǎn)你可能心中大打問號,這兩點(diǎn)不正是高效的精髓嗎?前后端邏輯復(fù)用,屏蔽數(shù)據(jù)庫細(xì)節(jié)。別急,讓我們重新用“理想的方式”來思考一下“邏輯”和“數(shù)據(jù)持久化”這兩件事。

數(shù)據(jù)與邏輯

我們以這樣一個問題開頭:任何一個應(yīng)用,我們的代碼最少能少到什么程度?

這算半個哲學(xué)問題。任何人想一想都會得到同一個答案:最少也就少到和應(yīng)用本身的描述一一對應(yīng)而已了。什么是應(yīng)用描述?或者說什么是應(yīng)用?我們會這樣描述一個博客:“用戶可以登錄、退出。用戶登錄后可以發(fā)表文章。發(fā)表文章時可以添加相應(yīng)的標(biāo)簽。”

抽象一下描述,答案很簡單:數(shù)據(jù),和邏輯。

如果你在一個流程要求嚴(yán)格的公司,應(yīng)用描述就是prd或系分文檔。應(yīng)用的數(shù)據(jù)就是數(shù)據(jù)字典,應(yīng)用的邏輯就是流程圖的總和:

流程圖

那么代碼最少能怎么寫呢?數(shù)據(jù)很簡單,參照數(shù)據(jù)字典,我們來用一種即使是產(chǎn)品經(jīng)理都能掌握的偽碼來寫:

 

  1. //描述字段 
  2. User : { name : string } Post : { title : string, content : text } Tag : { name : string } //描述關(guān)系 User -[created]-> Post Post -[has]-> Tag  

 

這里為了進(jìn)一步幫助讀者從已有的技術(shù)思維中跳出來,我想指出這段偽碼和數(shù)據(jù)庫字段描述有一個很大的區(qū)別,那就是:我不關(guān)心 User 和 Post 中間的關(guān)聯(lián)關(guān)系到底是在兩者的字段中都創(chuàng)建一個字段來保存對方的id,還是建立一個中間表。我只關(guān)心我描述它時的邏輯就夠了。數(shù)據(jù)描述的代碼,最簡也就簡 單到這個程度了。

那么邏輯呢?我們先用按常規(guī)方式試試?

 

  1. class User{ 
  2.     createPost( content, tags=[] ){         var post = new Post({content:content})            post.setTags( tags.map(tagName=>{ return new Tag(tagName)} ) )         return post        } }   

 

好像還不錯,如果今天產(chǎn)品經(jīng)理說我們增加一個 @ 功能,如果文章里 @ 某個用戶,那么我們就發(fā)個站內(nèi)信給他。

 

  1. class User{ 
  2.     createPost( content, tags=[] ){         var post = new Post({content:content})            post.setTags( tags.map(tagName=>{ return new Tag(tagName)} ) )         if( at.scan(content) ){             at.getUser(content).forEach( atUser =>{                 system.mail( atUser )             })         }         return post        } } 

 

你應(yīng)該意識到我要說什么了,像互聯(lián)網(wǎng)這種可以快到一天一個迭代的開發(fā)速度,如果沒有一個好的模式,可能用不了多久,新加的功能就把你的 createPost 搞成了800行。當(dāng)然,我也并不是要講設(shè)計模式。代碼中的設(shè)計模式,完全依賴于程序員本人,我們要思考的是從框架層面提供最簡單的寫法。

讓我們再回到哲學(xué)角度去分析一下業(yè)務(wù)邏輯。

我們所謂的邏輯,其實(shí)就是對一個 具體過程的描述 。在上面這個例子里,過程無非就是添加標(biāo)簽,全文掃描。描述一個過程,有兩個必備點(diǎn):

  - 干什么

#p#

- 順序

順序為什么是必備的?某天上面發(fā)了文件說標(biāo)題里帶 XXX 的文章都不能發(fā),于是你不得不在函數(shù)一開始時就進(jìn)行檢測,這時就必須指定順序。

如果我們用左右表示會互相影響的順序,從上下表示互不相干的順序,把上面的最初的流程圖重畫一下:

這是一棵樹。如果我們再加個功能,添加的標(biāo)簽如果是某個熱門標(biāo)簽,那么我們就把這篇文章放到網(wǎng)站的熱門推薦里。這棵樹會變成什么樣子呢:

是的,事實(shí)上人類思維中的任何過程,都可以畫成一棵樹。有條件的循環(huán)可以拆解成遞歸,最終也是一棵樹。但重點(diǎn)并不是樹本身,重點(diǎn)是上面這個例子演化 的過程,從一開始最簡單的需求,到加上一點(diǎn)新功能,再到加上一些惡心的特殊情況,這恰恰就是真實(shí)世界中 web 開發(fā)的縮影。真實(shí)世界中的變化更加頻繁可怕。其中最可怕的是,很多時候我們的程序結(jié)構(gòu)、用到的設(shè)計模式,都是適用于當(dāng)前的業(yè)務(wù)模型的。而某天業(yè)務(wù)模型變化 了,代碼質(zhì)量又不夠好的話,就可能遇到牽一發(fā)動全身,大廈將傾的噩夢。幾乎每個大公司都有一個“運(yùn)行時間長,維護(hù)的工程師換了一批又一批”的項目。 Amazon曾經(jīng)有個工程師描述維護(hù)這種項目的感覺:“climb the shit mountain”。

回到之前的話題,在邏輯處理上,我們的理想是寫出的代碼即短,又具有極高的可維護(hù)性和可擴(kuò)展性。

更具體一點(diǎn),可維護(hù)性,就是代碼和代碼結(jié)構(gòu),能***程度地反映業(yè)務(wù)邏輯。***我的代碼結(jié)構(gòu)在某種程度上看來和我們流程圖中的樹一樣。這樣我讀代碼, 就幾乎能理解業(yè)務(wù)邏輯。而可擴(kuò)展性,就是當(dāng)出現(xiàn)變化時,我能在完成變化時,能盡量少地去修改之前的代碼。同樣的,如果我們能保障代碼和代碼結(jié)構(gòu)能和流程圖 盡量一致,那么在修改時,圖上怎么改,我們代碼就怎么改。這也就是理論上能達(dá)到的最小修改度了。綜上,我們用什么樣的系統(tǒng)模型能把代碼變得像樹形結(jié)構(gòu)一 樣?

很簡單,事件系統(tǒng)就可以做到。我們把都一個業(yè)務(wù)邏輯當(dāng)做事件來觸發(fā),而具體需要執(zhí)行的操作單做監(jiān)聽器,那么上面的代碼就可以寫成:

 

  1. // emitter 是事件中心 
  2. emitter.on("post.create", function savePost(){...}) emitter.on("post.create", function createTags(){...}, {before:"savePost"}) emitter.on("post.create", function scanSensitiveWords( post ){ if( system.scanSensitiveWords( post ) ){ return new Error("you have sensitive words in post.") } }, {block:all}) emitter.on("post.create", function scanPopTags(){...})  

 

//執(zhí)行創(chuàng)建文章操作

emitter.fire("post.create", {...args})

這樣看來,每個操作的代碼變得職責(zé)單一,整體結(jié)構(gòu)也非常工整。值得注意的是,在這段偽碼里,我們用了 `{before:"savePost"}` 這樣的參數(shù)來表示操作的順序,看起來也和邏輯本身的描述一致。

讓我們回到可維護(hù)性和可擴(kuò)展性來檢查這種寫法。首先在可維護(hù)性上,代碼職責(zé)變得很清晰,并且與流程描述一致。不過也有一個問題,就是操作的執(zhí)行順序已經(jīng)無法給人宏觀上的印象,必須把每個監(jiān)聽器的順序參數(shù)拼起來,才能得到整體的順序。

在可擴(kuò)展性上,無路是新增還是刪除操作,對應(yīng)到代碼上都是刪除或新增相應(yīng)的一段,不會影響到其他操作代碼。我們甚至可以把這些代碼拆分到不同的文件中,當(dāng)做不同的模塊。這樣在增減功能時,就能通過增刪文件來實(shí)現(xiàn),這也為實(shí)現(xiàn)一個文件級的模塊管理器提供了基礎(chǔ)技術(shù)。

至此,除了無法在執(zhí)行順序上有一個宏觀印象這個問題,似乎我們得到了理想的描述邏輯的方式。那我們現(xiàn)在來攻克這***一個問題。拿目前的這段偽碼和之 前的比較,不難發(fā)現(xiàn),之前代碼需要被執(zhí)行一遍才能較好地得到其中函數(shù)的執(zhí)行順序,才能拿到一個調(diào)用棧。而現(xiàn)在的這段代碼,我只要實(shí)現(xiàn)一個簡單的 emitter,將代碼執(zhí)行一遍,就已經(jīng)能得到所有的監(jiān)聽器信息了。這樣我就能通過簡單的工具來得到這個宏觀的執(zhí)行順序,甚至以圖形化的方式展現(xiàn)出來。得 到的這張圖,不就是我們一模一樣的流程圖嗎?!

不知道你有沒有意識到,我們已經(jīng)打開了一扇之前不能打開的門!在之前的代碼中,我們是通過函數(shù)間的調(diào)用來組織邏輯的,這和我們現(xiàn)在的方式有一個很大的區(qū)別,那就是:用來封裝業(yè)務(wù)邏輯的函數(shù),和系統(tǒng)本身提供的其他函數(shù),沒有任何可以很好利用的區(qū)別,即使我們能得到函數(shù)的調(diào)用棧,這個調(diào)用棧用圖形化的方式打印出來也沒有意義,因為其中會參雜太多的無用函數(shù)信息,特別是當(dāng)我們還用了一些第三方類庫時。打印的結(jié)果可能是這樣:

而現(xiàn)在,我們用來表述業(yè)務(wù)的某個邏輯,就是事件。而相應(yīng)的操作,就是監(jiān)聽器。監(jiān)聽器無論是觸發(fā)還是注冊,都是通過 emitter 提供的函數(shù),那么我們只需要利用 emitter,就能打印出只有監(jiān)聽器的調(diào)用棧。而監(jiān)聽器的調(diào)用棧,就是我們的流程圖。

代碼結(jié)構(gòu)可圖形化,并且是有意義的可圖形化,這扇大門一旦打開,門后的財富是取之不盡的。我們從 開發(fā)、測試、監(jiān)控 三個方面來看我們能從中獲得什么。

在開發(fā)階段,我們可以通過調(diào)用棧生成圖,那通過圖來生成代碼還會難嗎?對于任何一份流程圖,我們都能輕易地直接生成代碼。然后填空就夠了。在調(diào)試 時、我們可以制作工具實(shí)時地打印出調(diào)用棧,甚至可以將調(diào)用時保存的傳入傳出值拿出來直接查看。這樣一旦出現(xiàn)問題,你就可以直接根據(jù)當(dāng)前保存的調(diào)用棧信息排 查問題,而再無需去重現(xiàn)它。同理,繁瑣的斷點(diǎn),四處打印的日志都可以告別了。

測試階段,既然能生成代碼,再自動生成測試用例也非常容易。我們可以通過工具直接檢測調(diào)用棧是否正確,也可以更細(xì)致地給定輸入值,然后檢測各個監(jiān)聽器的傳入傳出值是否正確。

同樣很容想到監(jiān)控,我們可以默認(rèn)將調(diào)用棧的數(shù)據(jù)建構(gòu)作為日志保留,再用系統(tǒng)的工具去掃描、對邊,就能自動實(shí)現(xiàn)對業(yè)務(wù)邏輯本身的監(jiān)控。

總結(jié)一下上述,用事件系統(tǒng)去描述邏輯、流程,使得我們代碼結(jié)構(gòu)和邏輯,能達(dá)到一個非常理想的對應(yīng)程度。這個對應(yīng)程度使得代碼里的調(diào)用棧信息就能表述邏輯。而這個調(diào)用棧所能產(chǎn)生的巨大價值,一方面在于可圖形化,另一方面則在于能實(shí)現(xiàn)測試、監(jiān)控等一系列工程領(lǐng)域的自動化。

到這里,我們已經(jīng)得到了兩種理想的表達(dá)方式來分別表述數(shù)據(jù)和邏輯。下面真正激動人心的時刻到了,我們來關(guān)注現(xiàn)實(shí)中的技術(shù),看是否真的能夠做出一個框架,讓我們能用一種革命性的方式來寫應(yīng)用?

#p#

理想到現(xiàn)實(shí)

首先來看數(shù)據(jù)描述語言和和數(shù)據(jù)持久化。你可能早已一眼看出 `User -[create]-> Post` 這樣的偽碼是來自圖數(shù)據(jù)庫 Neo4j 的查詢語言 cypher 。在這里我對不熟悉的讀者科普一下。Neo4j 是用 java 寫的開源圖數(shù)據(jù)庫。圖數(shù)據(jù)本身是以圖的方式去存儲數(shù)據(jù)。

例如同樣對于 User 這樣一個模型,在 關(guān)系型數(shù)據(jù)庫中就是一張表,每一行是一個 user 的數(shù)據(jù)。在圖數(shù)據(jù)庫中就是一堆節(jié)點(diǎn),每個節(jié)點(diǎn)是一個 user。當(dāng)我們又有了 Post 這個模型時,如果要表示用戶創(chuàng)建了 Post 這樣一個關(guān)系的話,在關(guān)系型數(shù)據(jù)庫里通常會建立一個中間表,存上相應(yīng) user 和 post 的 id。也或者直接在 user 或 post 表里增加一個字段,存上相應(yīng)的id。不同的方案適用于不同的場景。而 在圖數(shù)據(jù)庫中要表達(dá) user 和 post 的關(guān)系,就只有一種方式,那就是創(chuàng)建一個 user 到 post 的名為 CREATED 的 關(guān)系。這個關(guān)系還可以有屬性,比如 {createdAt:2016,client:"web"} 等。

你可以看出圖數(shù)據(jù)和關(guān)系型數(shù)據(jù)庫在使用上***的區(qū)別是,它讓你完全根據(jù)真實(shí)的邏輯去關(guān)聯(lián)兩個數(shù)據(jù)。而關(guān)系型數(shù)據(jù)庫則通常在使用時就已經(jīng)要根據(jù)使用場景、性能等因素做出不同的選擇。

我們再看查詢語言,在 SQL 中,我們是以`SELECT ... FROM` 這樣一種命令式地方式告訴數(shù)據(jù)怎樣給我我要的數(shù)據(jù)。語句的內(nèi)容和存數(shù)據(jù)的表結(jié)構(gòu)是耦合的。例如我要找出某個 user 創(chuàng)建的所有 post。表結(jié)構(gòu)設(shè)計得不同,那么查詢語句就不同。而在 Neo4js 的查詢語句 cypher 中,是以 `(User) -[CREATED] ->(Post)` 這樣的 模式匹配 的語句來進(jìn)行查詢的。這意味著,只要你能以人類語言描述自己想要的數(shù)據(jù),你就能自己翻譯成 cypher 進(jìn)行查詢。

除此之外,圖數(shù)據(jù)當(dāng)然還有很多高級特性。但對開發(fā)者來說,模式匹配式的查詢語句,才是真正革命性的技術(shù)。熟悉數(shù)據(jù)庫的讀者肯定有這樣的疑問:

其實(shí)很多 ORM 就能實(shí)現(xiàn) cypher 現(xiàn)在這樣的表達(dá)形式,但在很多大公司里,你會發(fā)現(xiàn)研發(fā)團(tuán)隊仍然堅持手寫 SQL 語句,而堅決不用 ORM。理由是,手寫 SQL 無論在排查問題還是優(yōu)化性能時,都是最快速的。特別是對于大產(chǎn)品來說,一個 SQL 就有可能節(jié)約或者損失巨額資產(chǎn)。所以寧愿用 “多人力、低效率” 去換 “性能和穩(wěn)定”,也不考慮 ORM。那么 cypher 如何面對這個問題?

確實(shí),cypher 可以在某種程度上理解成數(shù)據(jù)庫自帶的 ORM。它很難通過優(yōu)化查詢語句來提升性能,但可以通過其他方式。例如對耗時長的大查詢做數(shù)據(jù)緩存。或者把存儲分層,圖數(shù)據(jù)庫變成***層,中間針對某些應(yīng) 用場景來使用其他的數(shù)據(jù)庫做中間層。對有實(shí)力的團(tuán)隊來說,這個中間層甚至可以用類似于智能數(shù)據(jù)庫的方式來對線上查詢自動分析,自動實(shí)現(xiàn)中間層。事實(shí)上,這 些中間技術(shù)早就已經(jīng)成熟,結(jié)合上圖數(shù)據(jù)庫和cypher,是可以把傳統(tǒng)的“人力密集型開發(fā)”轉(zhuǎn)變?yōu)?ldquo;技術(shù)密集型開發(fā)”的。

扯得略遠(yuǎn)了,我們重新回到模式匹配型的查詢語句上,為什么說它是革命性的,因為它剛好滿足了我們之前對數(shù)據(jù)描述的需求。任何一個開發(fā)者,只要把數(shù)據(jù) 字典做出來。關(guān)于數(shù)據(jù)的工作就已經(jīng)完成了?;蛘邠Q個角度來說,在任何一個已有數(shù)據(jù)的系統(tǒng)中,只要我能在前端或者移動端中描述我想要的數(shù)據(jù),就能開發(fā)出應(yīng) 用,不再需要寫任何服務(wù)器端數(shù)據(jù)接口。Facebook 在 React Conf 上放出的前端 Relay 框架和 GraphQL 幾乎就已經(jīng)是這樣的實(shí)現(xiàn)。

再來看邏輯部分,無論在瀏覽器端還是服務(wù)器端,用什么語言,實(shí)現(xiàn)一個事件系統(tǒng)都再簡單不過。這里我們倒是可以進(jìn)一步探索,除了之前所說的圖形界面調(diào) 試,測試、監(jiān)控自動化,我們還能做什么?對前端來說,如果前后端事件系統(tǒng)可以直接打通,并且出錯時通過圖形化的調(diào)試工具能無需回滾直接排查,那就***了。

例如:在創(chuàng)建 post 的前端組件中

 

  1. //觸發(fā)前端的 post.create 事件 
  2. var post = {title: "test", content: "test"} emitter.fire("post.create").then(function(){ alert("創(chuàng)建成功") }).catch(function(){ alert("創(chuàng)建失敗") }) 

 

在處理邏輯的文件中:

 

  1. //可以增加前端專屬的邏輯 emitter.on("post.create", function checkTest(post){ if( post.title === "test"){ console.log("this is a test blog.") } }) //通過 server: 這樣的命名空間來觸發(fā)服務(wù)器端的事件 emitter.on("post.create", function communicateWithServer(post){ console.log("communicating with server") return emitter.fire("server:post.create", post) 
  2. })  

 

得到的事件棧

在瀏覽器端可以打通和服務(wù)器端的事件系統(tǒng),那么在服務(wù)器端呢?剛剛提到我們我們其實(shí)可以用任何自己熟悉的語言去實(shí)現(xiàn)事件系統(tǒng),那是不是也意味著,只要事件調(diào)用棧的數(shù)據(jù)格式一致,我們就可以做一個跨語言的架構(gòu)?

例如我們可以用nodejs的web框架作為服務(wù)器端入口,然后用python,用go去寫子系統(tǒng)。只要約定好系統(tǒng)間通信機(jī)制,以及事件調(diào)用棧的數(shù)據(jù)格式,那么就能實(shí)現(xiàn)跨語言的事件系統(tǒng)融合。這意味你未來看到的調(diào)用棧圖可能是:

跨語言的實(shí)現(xiàn),本身也是一筆巨大財富。例如當(dāng)我們未來想要找人一起協(xié)同完成某一個web應(yīng)用時,再也不必局限于某一種語言的實(shí)現(xiàn)。甚至利用 docker等容器技術(shù),執(zhí)行環(huán)境也不再是限制。再例如,當(dāng)系統(tǒng)負(fù)載增大,逐漸出現(xiàn)瓶頸時。我們可以輕松地使用更高效的語言或者執(zhí)行環(huán)境去替換掉某個業(yè)務(wù) 邏輯的監(jiān)聽器實(shí)現(xiàn)。

更多的例子,舉再多也舉不完。當(dāng)你真正自己想清楚這套架構(gòu)之后,你會發(fā)現(xiàn)未來已經(jīng)在你眼前。

到這里,對“理想”的想象和對實(shí)現(xiàn)技術(shù)的思考終于可以劃上句號了。對熟悉架構(gòu)的人來說,其實(shí)已經(jīng)圓滿了。但我也不想放棄來“求干貨”的觀眾們。下面演示的,就是在框架原型下開發(fā)的簡單應(yīng)用。這是一個多人的todo應(yīng)用。

前端基于react,后端基于koa。

#p#

目錄結(jié)構(gòu)

前端數(shù)據(jù)(todo 列表) /public/data/todos.js

前端邏輯(todo 基本邏輯) /public/events/todo.js

 

前端邏輯(輸入@時展示用戶列表) /public/events/mention.js

后端邏輯(通知被@用戶) /modules/mention.js

 

通過調(diào)試工具得到的創(chuàng)建時的調(diào)用棧和輸入@符號時的調(diào)用棧

這只是一個引子,目的是為了讓你宏觀的感受將應(yīng)用拆解為“數(shù)據(jù)+邏輯”以后能有多簡單。目前這套框架已完成 50% ,實(shí)現(xiàn)了數(shù)據(jù)部分的設(shè)計、前后端事件融合,還有跨語言等方案正在開發(fā)中。未來將開源,期待讀者關(guān)注。

后記

終于寫完了??蚣苤皇羌軜?gòu)的實(shí)現(xiàn)。這套架構(gòu)幾乎孕育了近兩年,這其中已經(jīng)開發(fā)出一款實(shí)現(xiàn)了部分功能,基于nodejs的服務(wù)器端原型框架。完整的框 架開發(fā)目前也已經(jīng)四個月了。雖然從它落地的這些前端技術(shù)、數(shù)據(jù)技術(shù)看起來,它其實(shí)是有技術(shù)基礎(chǔ)的,應(yīng)該是積累的產(chǎn)物。但實(shí)際上,最早的關(guān)于數(shù)據(jù)和邏輯的思 路,卻是在我讀研時對一個“很虛”的問題的思考:什么樣的系統(tǒng)是最靈活的系統(tǒng)?在很長一段時間內(nèi),對各種架構(gòu)的學(xué)習(xí)中我都沒有找到想要的答案,直到后來在 學(xué)認(rèn)知心理學(xué)和神經(jīng)學(xué)的時候,我想到了人。人是目前可以理解的***備適應(yīng)性,最靈活的系統(tǒng)。人是怎么運(yùn)作的?生理基礎(chǔ)是什么?

認(rèn)知心理學(xué)里提到曾經(jīng)有一個學(xué)派認(rèn)為人的任何行為都不過是對某種刺激的反射,這種刺激可以是來自內(nèi)部也可以是外部。來自內(nèi)部的刺激有兩個重要來源, 一是生理上,例如饑餓,疲憊。二則是記憶。例如,你每天起床要去工作,是因為你的過去的記憶告訴你你需要錢,或者你喜歡工作的內(nèi)容。這對人來說也是一種刺 激,所以你產(chǎn)生了去工作的動機(jī)。外部刺激就更簡單,例如生理上的被火燙了,心理上被嘲諷、被表揚(yáng)等等。而人的反應(yīng),就是對這些刺激而產(chǎn)生的多種反射的集 合。例如早上起床,你的一部分反射是產(chǎn)生上班的動機(jī),但是如果你生病了,你的身體和記憶就會刺激你去休息。最終你會在這兩種刺激下達(dá)到一個平衡,做出反 應(yīng)。值得注意的是,大部分時候,人在不同時間面臨相同的刺激,卻做出不同的反應(yīng)。并不是因為后來某些反射被刪除了,而是因為后來形成了更強(qiáng)的反射區(qū)壓制住 了之前的反射。它的生理基礎(chǔ)就是神經(jīng)學(xué)中的神經(jīng)遞質(zhì)可以互相壓制。

如果我們把要打造的系統(tǒng)看做一個有機(jī)體,把迭代看做生長,把用戶的使用看做不斷的刺激。那我們是不是就能模擬人的反射過程來打造系統(tǒng),從而期待系統(tǒng) 得到像人一樣的適應(yīng)力?而恰恰你會發(fā)現(xiàn)科幻作品中的人工智能產(chǎn)品通常都以人的形態(tài)出現(xiàn)。因為我們希望我們所使用的產(chǎn)品,就像人一樣通情達(dá)理,具有人一樣的 領(lǐng)悟能力。而要達(dá)到這樣的效果,或許就是不斷給給他添加人對刺激的反射規(guī)則。

思考到這一步的時候,我對應(yīng)用架構(gòu)的設(shè)計哲學(xué)已經(jīng)基本定型。后來驗證出來的,這樣的系統(tǒng)能夠極大地提高研發(fā)效率,都只是這段哲學(xué)的附加價值。其實(shí)提 高研發(fā)效率的原理很簡單,無論系統(tǒng)的需求再怎么擴(kuò)展、再怎么變更,它也是遵循人本身的思維邏輯的。因此,你始終可以使用本身就模擬人類認(rèn)知的系統(tǒng)去適應(yīng) 它。并且,它怎么變化,你就怎么變化。

架構(gòu)這種東西,最終仍然關(guān)注在使用者身上的。所以與其和我討論確定的技術(shù)問題,不如討論這些更有意義。對思考架構(gòu)的人來說,我認(rèn)為眼界和哲學(xué)高度,最重要。

#p#

討論記錄

尤小右:感覺其實(shí)就是 flux 啊,但是 string-based global event bus 規(guī)模大了還是會有點(diǎn)坑爹的。一個事件觸發(fā)的后果遍及全棧,不好 track。

答:和flux的區(qū)別在于flux的數(shù)據(jù)對象本身和對數(shù)據(jù)的操作是合在store里的。事件系統(tǒng)規(guī)模的問題通過兩個方式控制:一是命名空間。二是事件只應(yīng)用在業(yè)務(wù)邏輯個程度就夠了,像“存入數(shù)據(jù)庫”這種操作就不要再用事件觸發(fā)。這樣系統(tǒng)就不會亂掉,因為它只反映業(yè)務(wù)邏輯。

玉伯也叫黑俠:認(rèn)識心理學(xué)那段很有趣。很關(guān)注如何讓業(yè)務(wù)代碼隨著時間流逝不會腐化而會趨良?比如事件fire點(diǎn),怎么才能可控又夠用,而不會隨著業(yè) 務(wù)復(fù)雜而爆發(fā)式增長?(簡單如seajs, 隨著插件的多樣化事件點(diǎn)都經(jīng)常不夠用)。還有如何讓事件間彼此解耦?經(jīng)常一個需求要添加多個監(jiān)聽,做得不好還可能影響其他功能點(diǎn)。

答:用事件去反映業(yè)務(wù)邏輯,而不是技術(shù)實(shí)現(xiàn)的邏輯”不只是這套架構(gòu)對于防止事件濫用的一個建議,更是它的哲學(xué)理論的重要部分。遵守它,這套框架就能 把高可擴(kuò)展性和高可維護(hù)性發(fā)揮到***。我們用一個常見的例子來說明這一點(diǎn)。有時候面臨需求變更,我們會覺得難搞,會對產(chǎn)品經(jīng)理說:“你這個變更影響很大, 因為我的代碼中xxx不是這樣設(shè)計的”。而產(chǎn)品經(jīng)理有可能不理解,因為對他來說,變更的需求可能只是一個很簡單的邏輯,加上一點(diǎn)特殊情況而已。產(chǎn)生這種矛 盾的關(guān)鍵就在于,沒有找到一種能準(zhǔn)確描述業(yè)務(wù)邏輯的方式去組織代碼。如果組織代碼的方式和描述業(yè)務(wù)邏輯的方式一致,那么業(yè)務(wù)邏輯上覺得改動點(diǎn)很簡單,代碼 上就也會很簡單。這套架構(gòu)中的事件系統(tǒng)、包括事件擁有的順序控制等特性,都是為了提供一種盡可能合適的方式去描述業(yè)務(wù)邏輯。只有這樣,才能實(shí)現(xiàn)代碼最少、 最可讀、最可擴(kuò)展。它本身是為描述業(yè)務(wù)邏輯而不是技術(shù)實(shí)現(xiàn)邏輯而生。所以只有遵守這個規(guī)則,才能得到它帶來的財富。

玉伯也叫黑俠:嗯,看明白了。感覺是將代碼階段的復(fù)雜性,前移到了業(yè)務(wù)系分階段,如果系分階段做得好,那么代碼就會很優(yōu)雅。反之,則很難說。進(jìn)一步 提一個無恥要求:怎么保證系分階段的良好性呢?不少時候,寫代碼的過程,就是梳理業(yè)務(wù)邏輯的過程,寫完后,才明白某個需求真正該怎么實(shí)現(xiàn)。

答:不太認(rèn)同寫代碼的過程是梳理業(yè)務(wù)邏輯的過程??梢哉f寫代碼的過程是梳理具體技術(shù)實(shí)現(xiàn)的過程。如果一開始寫代碼的人連業(yè)務(wù)邏輯都不清楚,再好的技 術(shù)和框架也無法防止他寫出爛代碼。基于事件的架構(gòu)其實(shí)不是對系分的要求提高了,反而是降低了。因為只要求你理清楚邏輯,具體的實(shí)現(xiàn)寫得再爛,之后都可以依 賴事件系統(tǒng)架構(gòu)本身的靈活性去完善的。就例如“發(fā)表文章后給所有被@的人發(fā)站內(nèi)信”這樣的邏輯,你可能一開始沒有考慮發(fā)站內(nèi)信的時候***用個隊列,防止請 求被卡住。但只要你做到了最基礎(chǔ)的把“發(fā)送站內(nèi)”這個監(jiān)聽器注冊到“發(fā)表文章”的事件上。未來就能在不影響任何其他代碼的情況下去優(yōu)化。實(shí)際上沒有任何框 架能幫你寫好代碼,即使DDD社區(qū)也是強(qiáng)調(diào)不斷重構(gòu),只可能“降低讓你寫好代碼的門檻”。這套架構(gòu)就是屏蔽很多技術(shù)上的概念,用事件的方式讓你只關(guān)注邏 輯。

玉伯也叫黑俠:有沒有一種讓代碼趨良的架構(gòu)?可能剛開始寫得亂糟糟,但隨著做的需求越多,寫的代碼越多,整體可維護(hù)性反而會變得越好?比如前后端分 層,讓后端專注業(yè)務(wù)模型,一般來說,業(yè)務(wù)模型會逐步趨于完善和穩(wěn)定,前端代碼也會逐步變好。用一些約束,推動代碼的良性循環(huán)。這些約束,是否就是理想應(yīng)用 架構(gòu)的精髓?這些約束是什么?可能是某種要求比如測試覆蓋率,也可能是某種強(qiáng)制約束比如必須通過數(shù)據(jù)改動來更新界面。roof的約束是用事件去反映業(yè)務(wù)邏 輯,但這個約束更多是「道德」層面,而不是「法律」,比如如何防止「大事件」(一個事件里,一坨技術(shù)實(shí)現(xiàn)的邏輯代碼)?如何讓人羞于去寫出糟糕的代碼?

答:即使前后端分離,業(yè)務(wù)模型趨于穩(wěn)定,也是靠開發(fā)者自身不斷重構(gòu)去實(shí)現(xiàn)的,要不然怎么會“趨于”穩(wěn)定呢。架構(gòu)只可能讓人站到更好地平臺上,用更好 地方式去寫好代碼,不可能主動幫人把代碼變好。文中架構(gòu)就是通過屏蔽技術(shù)細(xì)節(jié),讓你關(guān)注業(yè)務(wù)邏輯的方式,讓代碼易理解,也讓你能不影響業(yè)務(wù)地去升級技術(shù)。 這套架構(gòu)因為有一個清晰的事件調(diào)用棧數(shù)據(jù)結(jié)構(gòu),所以能很容易地做出相應(yīng)的測試、監(jiān)控工具保障代碼質(zhì)量。但要實(shí)現(xiàn)“法律”是不可能的。即使是Java、即使 是領(lǐng)域驅(qū)動編程,也可以在它好的架構(gòu)下寫出各種糟糕的代碼。畢竟編程仍然是一件需要創(chuàng)造力的工作。這就像硬幣的兩面,如果要實(shí)現(xiàn)法律,那工作本身必須是無 需創(chuàng)造,完全可以按照流程由機(jī)器人生產(chǎn)。如果要創(chuàng)造力,就必然會有因人而異的品質(zhì)差異。

責(zé)任編輯:王雪燕 來源: 博客園
相關(guān)推薦

2009-09-03 16:52:44

Java Web開發(fā)框

2021-02-18 09:54:37

數(shù)據(jù)湖框架數(shù)據(jù)

2013-07-12 10:21:34

云時代云服務(wù)IT

2010-08-05 13:50:54

Flex框架

2010-08-10 14:53:32

FlexRiawave

2015-05-12 10:15:15

程序員

2010-08-13 14:29:45

Flex框架

2013-09-16 09:16:42

云成本云成本節(jié)省云應(yīng)用

2009-10-13 11:32:19

Winform假框架

2009-07-14 18:10:38

Swing應(yīng)用程序框架

2020-01-11 17:49:03

區(qū)塊鏈數(shù)字貨幣比特幣

2009-06-29 17:17:57

Spring

2009-06-19 09:52:46

Acegi安全框架Spring框架

2022-08-06 16:40:13

SDN網(wǎng)絡(luò)

2018-09-06 15:15:44

2014-10-21 10:33:51

數(shù)據(jù)保護(hù)云存儲單點(diǎn)故障

2012-09-28 11:48:45

IBM

2017-09-20 08:45:58

2015-04-21 12:48:37

老碼農(nóng)技術(shù)理想

2018-12-05 15:55:44

硬件虛擬化kernel
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號