程序員心中最佳的框架組合

在過去對框架的設計中,我收到過的最有用的建議是:「不要一開始就根據現有的技術去整合和改進。而是先搞清楚你以爲最理想的框架應該是怎樣的,再根據如今的技術去評估,的確實現不了時再妥協。這樣才能作出真正有意義的框架。」
在這篇文章裏,就讓咱們按照這樣一條建議來探索一下如今的 web 框架最終能夠進化成的樣子,你絕對會被驚豔到。前端

前端,仍是從前端提及。前端目前的現狀是,隨着早期的 Backbone,近期的 Angular、React 等框架的興起,前端在 模塊化、組件化 兩個方向上已經造成了必定的行業共識。在此基礎上,React 的 FLUX、Relay 則是進一步的對前端應用架構的探索。這些技術在目前國內的大公司、大團隊內部實際上都落地得很是好,由於很容易和公司內部已有的後端技術棧結合。並且這些純前端框架的配套技術方案通常比較成熟,例如在支付寶肯定使用 React,其實有一部分緣由是它兼容 IE8,而且有服務器端渲染方案來加速首屏。java

相比之下,像 Meteor 這類從前到後包辦的框架就較難落地。雖然能極大地提升開發效率,總體架構很是先進,但架構的每個層級每每不容易達到行業內的頂尖標準。特別是在服務器端,對大公司來講,一般都有適合本身業務的服務器集羣、數據庫方案,而且經受過考驗。所以當一個團隊一上手就要作面向十萬級、甚至百萬級用戶的產品時,是不太願意冒風險去嘗試的。反而是我的開發者、創業型的團隊會願意去用,由於確實能在短期內高效地開發出可用的產品出來。包括像 Leancloud 提出的這類型的服務,也是很是受歡迎的。node

這種現狀,就是理想和現實的一個爭論。Meteor 的方式能知足我對開發效率的理想,而團隊已有的技術方案能保障穩定。可否整合其中的優點,不妨讓咱們進一步來細化一下對框架的但願:python

- 有強大的先後端一致的數據模型層
- 代碼能夠能夠複用。例如我有一個 User 模型,當我建立一個新的 user 時,user 上的字段驗證等方法是先後端通用的,由框架自動幫我區別先後端環境。
- 數據模型和前端框架沒有耦合,但能夠輕鬆結合。這樣在前端渲染型的框架進一步升級時,不影響個人業務邏輯代碼。
- 由數據模型層提供自動的數據更新機制。例如我在前端要獲取 id 爲 1 的用戶,而且若是服務器端數據有更新的話,就自動幫我更新,不須要我本身去實現輪詢。我但願的代碼寫法是:react

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

實際上,Meteor已經能實現絕大部分上述功能。但這不是軟文。我要強調兩點我不但願的:程序員

- 我不但願這個數據模型層去包含業務邏輯,也就是我建立的user對象,我不但願它提供 login、logout 等 api。
- 我也不但願數據模型層自動和任何ORM框架綁定,提供任何 SQL 或 NoSQL 的數據支持。

看到這兩點你可能心中大打問號,這兩點不正是高效的精髓嗎?先後端邏輯複用,屏蔽數據庫細節。別急,讓咱們從新用「理想的方式」來思考一下「邏輯」和「數據持久化」這兩件事。web

數據與邏輯

咱們以這樣一個問題開頭:任何一個應用,咱們的代碼最少能少到什麼程度?docker

這算半個哲學問題。任何人想想都會獲得同一個答案:最少也就少到和應用自己的描述一一對應而已了。什麼是應用描述?或者說什麼是應用?咱們會這樣描述一個博客:「用戶能夠登陸、退出。用戶登陸後能夠發表文章。發表文章時能夠添加相應的標籤。」數據庫

抽象一下描述,答案很簡單:數據,和邏輯。編程

若是你在一個流程要求嚴格的公司,應用描述就是prd或系分文檔。應用的數據就是數據字典,應用的邏輯就是流程圖的總和:

 

 

 

流程圖

 

 

那麼代碼最少能怎麼寫呢?數據很簡單,參照數據字典,咱們來用一種即便是產品經理都能掌握的僞碼來寫:

User : {
name : string
}

Post : {
title : string,
content : text
}

Tag : {
name : string
}


User -[created]-> Post
Post -[has]-> Tag

這裏爲了進一步幫助讀者從已有的技術思惟中跳出來,我想指出這段僞碼和數據庫字段描述有一個很大的區別,那就是:我不關心 User 和 Post 中間的關聯關係究竟是在二者的字段中都建立一個字段來保存對方的id,仍是創建一箇中間表。我只關心我描述它時的邏輯就夠了。數據描述的代碼,最簡也就簡單到這個程度了。

那麼邏輯呢?咱們先用按常規方式試試?

class User{
    createPost( content, tags= post =  Post({content:content})    
        post.setTags( tags.map(tagName=>{ return  Tag(tagName)} ) )
        return post    
    }
}        

好像還不錯,若是今天產品經理說咱們增長一個 @ 功能,若是文章裏 @ 某個用戶,那麼咱們就發個站內信給他。

class User{
    createPost( content, tags= post =  Post({content:content})    
        post.setTags( tags.map(tagName=>{ return  Tag(tagName)} ) )

        ( at.scan(content) ){
            at.getUser(content).forEach( atUser =>{
                system.mail( atUser )
            })
        }

        return post    
    }
}

你應該意識到我要說什麼了,像互聯網這種能夠快到一天一個迭代的開發速度,若是沒有一個好的模式,可能用不了多久,新加的功能就把你的 createPost 搞成了800行。固然,我也並非要講設計模式。代碼中的設計模式,徹底依賴於程序員本人,咱們要思考的是從框架層面提供最簡單的寫法。

讓咱們再回到哲學角度去分析一下業務邏輯。
咱們所謂的邏輯,其實就是對一個 具體過程的描述 。在上面這個例子裏,過程無非就是添加標籤,全文掃描。描述一個過程,有兩個必備點:

  - 幹什麼
  - 順序

順序爲何是必備的?某天上面發了文件說標題裏帶 XXX 的文章都不能發,因而你不得不在函數一開始時就進行檢測,這時就必須指定順序。

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

 

 

 

這是一棵樹。若是咱們再加個功能,添加的標籤若是是某個熱門標籤,那麼咱們就把這篇文章放到網站的熱門推薦裏。這棵樹會變成什麼樣子呢:

 

 

是的,事實上人類思惟中的任何過程,均可以畫成一棵樹。有條件的循環能夠拆解成遞歸,最終也是一棵樹。但重點並非樹自己,重點是上面這個例子演化的過程,從一開始最簡單的需求,到加上一點新功能,再到加上一些噁心的特殊狀況,這偏偏就是真實世界中 web 開發的縮影。真實世界中的變化更加頻繁可怕。其中最可怕的是,不少時候咱們的程序結構、用到的設計模式,都是適用於當前的業務模型的。而某天業務模型變化了,代碼質量又不夠好的話,就可能遇到牽一髮動全身,大廈將傾的噩夢。幾乎每一個大公司都有一個「運行時間長,維護的工程師換了一批又一批」的項目。Amazon曾經有個工程師描述維護這種項目的感受:「climb the shit mountain」。

回到以前的話題,在邏輯處理上,咱們的理想是寫出的代碼即短,又具備極高的可維護性和可擴展性。

更具體一點,可維護性,就是代碼和代碼結構,能最大程度地反映業務邏輯。最好個人代碼結構在某種程度上看來和咱們流程圖中的樹同樣。這樣我讀代碼,就幾乎能理解業務邏輯。而可擴展性,就是當出現變化時,我能在完成變化時,能儘可能少地去修改以前的代碼。一樣的,若是咱們能保障代碼和代碼結構能和流程圖儘可能一致,那麼在修改時,圖上怎麼改,咱們代碼就怎麼改。這也就是理論上能達到的最小修改度了。綜上,咱們用什麼樣的系統模型能把代碼變得像樹形結構同樣?

很簡單,事件系統就能夠作到。咱們把都一個業務邏輯當作事件來觸發,而具體須要執行的操做單作監聽器,那麼上面的代碼就能夠寫成:

 emitter 是事件中心

emitter.on("post.create", function savePost(){...})

emitter.on("post.create", function createTags(){...}, {before:"savePost"})

emitter.on("post.create", function scanSensitiveWords( post ){

    ( system.scanSensitiveWords( post ) ){
        return  Error("you have sensitive words in post.")
    }

}, {block:all})

emitter.on("post.create", function scanPopTags(){...})
執行建立文章操做
emitter.fire("post.create", {...args})

這樣看來,每一個操做的代碼變得職責單一,總體結構也很是工整。值得注意的是,在這段僞碼裏,咱們用了 `{before:"savePost"}` 這樣的參數來表示操做的順序,看起來也和邏輯自己的描述一致。

讓咱們回到可維護性和可擴展性來檢查這種寫法。首先在可維護性上,代碼職責變得很清晰,而且與流程描述一致。不過也有一個問題,就是操做的執行順序已經沒法給人宏觀上的印象,必須把每一個監聽器的順序參數拼起來,才能獲得總體的順序。

在可擴展性上,無路是新增仍是刪除操做,對應到代碼上都是刪除或新增相應的一段,不會影響到其餘操做代碼。咱們甚至能夠把這些代碼拆分到不一樣的文件中,當作不一樣的模塊。這樣在增減功能時,就能經過增刪文件來實現,這也爲實現一個文件級的模塊管理器提供了基礎技術。

至此,除了沒法在執行順序上有一個宏觀印象這個問題,彷佛咱們獲得了理想的描述邏輯的方式。那咱們如今來攻克這最後一個問題。拿目前的這段僞碼和以前的比較,不難發現,以前代碼須要被執行一遍才能較好地獲得其中函數的執行順序,才能拿到一個調用棧。而如今的這段代碼,我只要實現一個簡單的 emitter,將代碼執行一遍,就已經能獲得全部的監聽器信息了。這樣我就能經過簡單的工具來獲得這個宏觀的執行順序,甚至以圖形化的方式展示出來。獲得的這張圖,不就是咱們如出一轍的流程圖嗎?!

不知道你有沒有意識到,咱們已經打開了一扇以前不能打開的門!在以前的代碼中,咱們是經過函數間的調用來組織邏輯的,這和咱們如今的方式有一個很大的區別,那就是:用來封裝業務邏輯的函數,和系統自己提供的其餘函數,沒有任何能夠很好利用的區別,即便咱們能獲得函數的調用棧,這個調用棧用圖形化的方式打印出來也沒有意義,由於其中會參雜太多的無用函數信息,特別是當咱們還用了一些第三方類庫時。打印的結果多是這樣:

 

 

 

而如今,咱們用來表述業務的某個邏輯,就是事件。而相應的操做,就是監聽器。監聽器不管是觸發仍是註冊,都是經過 emitter 提供的函數,那麼咱們只須要利用 emitter,就能打印出只有監聽器的調用棧。而監聽器的調用棧,就是咱們的流程圖。

 

 

 

代碼結構可圖形化,而且是有意義的可圖形化,這扇大門一旦打開,門後的財富是取之不盡的。咱們從 開發、測試、監控 三個方面來看咱們能從中得到什麼。

在開發階段,咱們能夠經過調用棧生成圖,那經過圖來生成代碼還會難嗎?對於任何一份流程圖,咱們都能輕易地直接生成代碼。而後填空就夠了。在調試時、咱們能夠製做工具實時地打印出調用棧,甚至能夠將調用時保存的傳入傳出值拿出來直接查看。這樣一旦出現問題,你就能夠直接根據當前保存的調用棧信息排查問題,而再無需去重現它。同理,繁瑣的斷點,四處打印的日誌均可以告別了。

測試階段,既然能生成代碼,再自動生成測試用例也很是容易。咱們能夠經過工具直接檢測調用棧是否正確,也能夠更細緻地給定輸入值,而後檢測各個監聽器的傳入傳出值是否正確。

一樣很容想到監控,咱們能夠默認將調用棧的數據建構做爲日誌保留,再用系統的工具去掃描、對邊,就能自動實現對業務邏輯自己的監控。

總結一下上述,用事件系統去描述邏輯、流程,使得咱們代碼結構和邏輯,能達到一個很是理想的對應程度。這個對應程度使得代碼裏的調用棧信息就能表述邏輯。而這個調用棧所能產生的巨大價值,一方面在於可圖形化,另外一方面則在於能實現測試、監控等一系列工程領域的自動化。

到這裏,咱們已經獲得了兩種理想的表達方式來分別表述數據和邏輯。下面真正激動人心的時刻到了,咱們來關注現實中的技術,看是否真的可以作出一個框架,讓咱們能用一種革命性的方式來寫應用?

理想到現實

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

例如一樣對於 User 這樣一個模型,在 關係型數據庫中就是一張表,每一行是一個 user 的數據。在圖數據庫中就是一堆節點,每一個節點是一個 user。當咱們又有了 Post 這個模型時,若是要表示用戶建立了 Post 這樣一個關係的話,在關係型數據庫裏一般會創建一箇中間表,存上相應 user 和 post 的 id。也或者直接在 user 或 post 表裏增長一個字段,存上相應的id。不一樣的方案適用於不一樣的場景。而 在圖數據庫中要表達 user 和 post 的關係,就只有一種方式,那就是建立一個 user 到 post 的名爲 CREATED 的 關係。這個關係還能夠有屬性,好比 {createdAt:2016,client:"web"} 等。

你能夠看出圖數據和關係型數據庫在使用上最大的區別是,它讓你徹底根據真實的邏輯去關聯兩個數據。而關係型數據庫則一般在使用時就已經要根據使用場景、性能等因素作出不一樣的選擇。

咱們再看查詢語言,在 SQL 中,咱們是以`SELECT ... FROM` 這樣一種命令式地方式告訴數據怎樣給我我要的數據。語句的內容和存數據的表結構是耦合的。例如我要找出某個 user 建立的全部 post。表結構設計得不一樣,那麼查詢語句就不一樣。而在 Neo4js 的查詢語句 cypher 中,是以 `(User) -[CREATED] ->(Post)` 這樣的 模式匹配 的語句來進行查詢的。這意味着,只要你能以人類語言描述本身想要的數據,你就能本身翻譯成 cypher 進行查詢。

除此以外,圖數據固然還有不少高級特性。但對開發者來講,模式匹配式的查詢語句,纔是真正革命性的技術。熟悉數據庫的讀者確定有這樣的疑問:

其實不少 ORM 就能實現 cypher 如今這樣的表達形式,但在不少大公司裏,你會發現研發團隊仍然堅持手寫 SQL 語句,而堅定不用 ORM。理由是,手寫 SQL 不管在排查問題仍是優化性能時,都是最快速的。特別是對於大產品來講,一個 SQL 就有可能節約或者損失鉅額資產。因此寧願用 「多人力、低效率」 去換 「性能和穩定」,也不考慮 ORM。那麼 cypher 如何面對這個問題?

確實,cypher 能夠在某種程度上理解成數據庫自帶的 ORM。它很難經過優化查詢語句來提高性能,但能夠經過其餘方式。例如對耗時長的大查詢作數據緩存。或者把存儲分層,圖數據庫變成最底層,中間針對某些應用場景來使用其餘的數據庫作中間層。對有實力的團隊來講,這個中間層甚至能夠用相似於智能數據庫的方式來對線上查詢自動分析,自動實現中間層。事實上,這些中間技術早就已經成熟,結合上圖數據庫和cypher,是能夠把傳統的「人力密集型開發」轉變爲「技術密集型開發」的。

扯得略遠了,咱們從新回到模式匹配型的查詢語句上,爲何說它是革命性的,由於它恰好知足了咱們以前對數據描述的需求。任何一個開發者,只要把數據字典作出來。關於數據的工做就已經完成了。或者換個角度來講,在任何一個已有數據的系統中,只要我能在前端或者移動端中描述我想要的數據,就能開發出應用,再也不須要寫任何服務器端數據接口。Facebook 在 React Conf 上放出的前端 Relay 框架和 GraphQL 幾乎就已是這樣的實現。

再來看邏輯部分,不管在瀏覽器端仍是服務器端,用什麼語言,實現一個事件系統都再簡單不過。這裏咱們卻是能夠進一步探索,除了以前所說的圖形界面調試,測試、監控自動化,咱們還能作什麼?對前端來講,若是先後端事件系統能夠直接打通,而且出錯時經過圖形化的調試工具能無需回滾直接排查,那就最好了。
例如:在建立 post 的前端組件中

觸發前端的 post.create 事件
 post = {title: "test", content: "test"emitter.fire("post.create").then(function(){
    alert("建立成功"catch(function(){
    alert("建立失敗"

在處理邏輯的文件中:

能夠增長前端專屬的邏輯
emitter.on("post.create", function checkTest(post){
    ( post.title === "test"){
        console.log("this is a test blog."經過 server: 這樣的命名空間來觸發服務器端的事件
emitter.on("post.create", function communicateWithServer(post){
    console.log("communicating with server"return emitter.fire("server:post.create", post)
})

獲得的事件棧

 

 

 

在瀏覽器端能夠打通和服務器端的事件系統,那麼在服務器端呢?剛剛提到咱們咱們其實能夠用任何本身熟悉的語言去實現事件系統,那是否是也意味着,只要事件調用棧的數據格式一致,咱們就能夠作一個跨語言的架構?

例如咱們能夠用nodejs的web框架做爲服務器端入口,而後用python,用go去寫子系統。只要約定好系統間通訊機制,以及事件調用棧的數據格式,那麼就能實現跨語言的事件系統融合。這意味你將來看到的調用棧圖多是:

 

 

 

跨語言的實現,自己也是一筆巨大財富。例如當咱們將來想要找人一塊兒協同完成某一個web應用時,不再必侷限於某一種語言的實現。甚至利用docker等容器技術,執行環境也再也不是限制。再例如,當系統負載增大,逐漸出現瓶頸時。咱們能夠輕鬆地使用更高效的語言或者執行環境去替換掉某個業務邏輯的監聽器實現。

更多的例子,舉再多也舉不完。當你真正本身想清楚這套架構以後,你會發現將來已經在你眼前。

到這裏,對「理想」的想象和對實現技術的思考終於能夠劃上句號了。對熟悉架構的人來講,其實已經圓滿了。但我也不想放棄來「求乾貨」的觀衆們。下面演示的,就是在框架原型下開發的簡單應用。這是一個多人的todo應用。

 

 

 

前端基於react,後端基於koa。

目錄結構

 

 

 

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

 

 

 

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

 

 

 

 

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

 

 

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

 

 

 

經過調試工具獲得的建立時的調用棧和輸入@符號時的調用棧

 

 

 

這只是一個引子,目的是爲了讓你宏觀的感覺將應用拆解爲「數據+邏輯」之後能有多簡單。目前這套框架已完成 50% ,實現了數據部分的設計、先後端事件融合,還有跨語言等方案正在開發中。將來將開源,期待讀者關注。

終於寫完了。框架只是架構的實現。這套架構幾乎孕育了近兩年,這其中已經開發出一款實現了部分功能,基於nodejs的服務器端原型框架。完整的框架開發目前也已經四個月了。雖然從它落地的這些前端技術、數據技術看起來,它實際上是有技術基礎的,應該是積累的產物。但實際上,最先的關於數據和邏輯的思路,倒是在我讀研時對一個「很虛」的問題的思考:什麼樣的系統是最靈活的系統?在很長一段時間內,對各類架構的學習中我都沒有找到想要的答案,直到後來在學認知心理學和神經學的時候,我想到了人。人是目前能夠理解的最具有適應性,最靈活的系統。人是怎麼運做的?生理基礎是什麼?

認知心理學裏提到曾經有一個學派認爲人的任何行爲都不過是對某種刺激的反射,這種刺激能夠是來自內部也能夠是外部。來自內部的刺激有兩個重要來源,一是生理上,例如飢餓,疲憊。二則是記憶。例如,你天天起牀要去工做,是由於你的過去的記憶告訴你你須要錢,或者你喜歡工做的內容。這對人來講也是一種刺激,因此你產生了去工做的動機。外部刺激就更簡單,例如生理上的被火燙了,心理上被嘲諷、被表揚等等。而人的反應,就是對這些刺激而產生的多種反射的集合。例如早上起牀,你的一部分反射是產生上班的動機,可是若是你生病了,你的身體和記憶就會刺激你去休息。最終你會在這兩種刺激下達到一個平衡,作出反應。值得注意的是,大部分時候,人在不一樣時間面臨相同的刺激,卻作出不一樣的反應。並非由於後來某些反射被刪除了,而是由於後來造成了更強的反射區壓制住了以前的反射。它的生理基礎就是神經學中的神經遞質能夠互相壓制。

若是咱們把要打造的系統看作一個有機體,把迭代看作生長,把用戶的使用看作不斷的刺激。那咱們是否是就能模擬人的反射過程來打造系統,從而期待系統獲得像人同樣的適應力?而偏偏你會發現科幻做品中的人工智能產品一般都以人的形態出現。由於咱們但願咱們所使用的產品,就像人同樣通情達理,具備人同樣的領悟能力。而要達到這樣的效果,或許就是不斷給給他添加人對刺激的反射規則。

思考到這一步的時候,我對應用架構的設計哲學已經基本定型。後來驗證出來的,這樣的系統可以極大地提升研發效率,都只是這段哲學的附加價值。其實提升研發效率的原理很簡單,不管系統的需求再怎麼擴展、再怎麼變動,它也是遵循人自己的思惟邏輯的。所以,你始終可使用自己就模擬人類認知的系統去適應它。而且,它怎麼變化,你就怎麼變化。

架構這種東西,最終仍然關注在使用者身上的。因此與其和我討論肯定的技術問題,不如討論這些更有意義。對思考架構的人來講,我認爲眼界和哲學高度,最重要。

尤小右:感受其實就是 flux 啊,可是 string-based global event bus 規模大了仍是會有點坑爹的。一個事件觸發的後果遍佈全棧,很差 track。

答:和flux的區別在於flux的數據對象自己和對數據的操做是合在store裏的。事件系統規模的問題經過兩個方式控制:一是命名空間。二是事件只應用在業務邏輯個程度就夠了,像「存入數據庫」這種操做就不要再用事件觸發。這樣系統就不會亂掉,由於它只反映業務邏輯。

玉伯也叫黑俠:認識心理學那段頗有趣。很關注如何讓業務代碼隨着時間流逝不會腐化而會趨良?好比事件fire點,怎麼才能可控又夠用,而不會隨着業務複雜而爆發式增加?(簡單如seajs, 隨着插件的多樣化事件點都常常不夠用)。還有如何讓事件間彼此解耦?常常一個需求要添加多個監聽,作得很差還可能影響其餘功能點。

答:用事件去反映業務邏輯,而不是技術實現的邏輯」不僅是這套架構對於防止事件濫用的一個建議,更是它的哲學理論的重要部分。遵照它,這套框架就能把高可擴展性和高可維護性發揮到極致。咱們用一個常見的例子來講明這一點。有時候面臨需求變動,咱們會以爲難搞,會對產品經理說:「你這個變動影響很大,由於個人代碼中xxx不是這樣設計的」。而產品經理有可能不理解,由於對他來講,變動的需求可能只是一個很簡單的邏輯,加上一點特殊狀況而已。產生這種矛盾的關鍵就在於,沒有找到一種能準確描述業務邏輯的方式去組織代碼。若是組織代碼的方式和描述業務邏輯的方式一致,那麼業務邏輯上以爲改動點很簡單,代碼上就也會很簡單。這套架構中的事件系統、包括事件擁有的順序控制等特性,都是爲了提供一種儘量合適的方式去描述業務邏輯。只有這樣,才能實現代碼最少、最可讀、最可擴展。它自己是爲描述業務邏輯而不是技術實現邏輯而生。因此只有遵照這個規則,才能獲得它帶來的財富。

玉伯也叫黑俠:嗯,看明白了。感受是將代碼階段的複雜性,前移到了業務系分階段,若是系分階段作得好,那麼代碼就會很優雅。反之,則很難說。進一步提一個無恥要求:怎麼保證系分階段的良好性呢?很多時候,寫代碼的過程,就是梳理業務邏輯的過程,寫完後,才明白某個需求真正該怎麼實現。

答:不太認同寫代碼的過程是梳理業務邏輯的過程。能夠說寫代碼的過程是梳理具體技術實現的過程。若是一開始寫代碼的人連業務邏輯都不清楚,再好的技術和框架也沒法防止他寫出爛代碼。基於事件的架構其實不是對系分的要求提升了,反而是下降了。由於只要求你理清楚邏輯,具體的實現寫得再爛,以後均可以依賴事件系統架構自己的靈活性去完善的。就例如「發表文章後給全部被@的人發站內信」這樣的邏輯,你可能一開始沒有考慮發站內信的時候最好用個隊列,防止請求被卡住。但只要你作到了最基礎的把「發送站內」這個監聽器註冊到「發表文章」的事件上。將來就能在不影響任何其餘代碼的狀況下去優化。實際上沒有任何框架能幫你寫好代碼,即便DDD社區也是強調不斷重構,只可能「下降讓你寫好代碼的門檻」。這套架構就是屏蔽不少技術上的概念,用事件的方式讓你只關注邏輯。

玉伯也叫黑俠:有沒有一種讓代碼趨良的架構?可能剛開始寫得亂糟糟,但隨着作的需求越多,寫的代碼越多,總體可維護性反而會變得越好?好比先後端分層,讓後端專一業務模型,通常來講,業務模型會逐步趨於完善和穩定,前端代碼也會逐步變好。用一些約束,推進代碼的良性循環。這些約束,是否就是理想應用架構的精髓?這些約束是什麼?多是某種要求好比測試覆蓋率,也多是某種強制約束好比必須經過數據改動來更新界面。roof的約束是用事件去反映業務邏輯,但這個約束更可能是「道德」層面,而不是「法律」,好比如何防止「大事件」(一個事件裏,一坨技術實現的邏輯代碼)?如何讓人羞於去寫出糟糕的代碼?

答:即便先後端分離,業務模型趨於穩定,也是靠開發者自身不斷重構去實現的,要否則怎麼會「趨於」穩定呢。架構只可能讓人站到更好地平臺上,用更好地方式去寫好代碼,不可能主動幫人把代碼變好。文中架構就是經過屏蔽技術細節,讓你關注業務邏輯的方式,讓代碼易理解,也讓你能不影響業務地去升級技術。這套架構由於有一個清晰的事件調用棧數據結構,因此能很容易地作出相應的測試、監控工具保障代碼質量。但要實現「法律」是不可能的。即便是Java、即便是領域驅動編程,也能夠在它好的架構下寫出各類糟糕的代碼。畢竟編程仍然是一件須要創造力的工做。這就像硬幣的兩面,若是要實現法律,那工做自己必須是無需創造,徹底能夠按照流程由機器人生產。若是要創造力,就必然會有因人而異的品質差別。

 

全能程序員交流QQ羣290551701,羣內程序員都是來自,百度、阿里、京東、小米、去哪兒、餓了嗎、藍港等高級程序員 ,擁有豐富的經驗。加入咱們,直線溝通技術大牛,最佳的學習環境,瞭解業內的一手的資訊。若是你想結實大牛,那 就加入進來,讓大牛帶你超神!

相關文章
相關標籤/搜索