(本文不適合初學(xué)者閱讀,目前只是為了方便培訓(xùn)的時(shí)候預(yù)習(xí)而寫熙涤,也不適合無后續(xù)服務(wù)的人閱讀)
現(xiàn)在,我們把上一篇的應(yīng)用變成網(wǎng)絡(luò)版随珠。這個(gè)時(shí)候,你至少有了兩個(gè)應(yīng)用窗看,一個(gè)客戶端應(yīng)用,一個(gè)服務(wù)端應(yīng)用显沈。到這一刻软瞎,我們就算具有了一個(gè)系統(tǒng)拉讯。
當(dāng)我們有一個(gè)系統(tǒng)的時(shí)候,我們需要一種框架來簡(jiǎn)化思考我們的應(yīng)用魔慷。這里又需要我們?cè)俅握故疚覀兊母拍钚运伎寄芰χ欢В@時(shí)我會(huì)采用Linux的模型來思考這個(gè)問題院尔,所以應(yīng)用程序一般我會(huì)分為三層:
core層是我的核心邏輯喉誊,核心的計(jì)算部分放在這里。(Linux里是Kernel伍茄,不過Kernel這詞比較偏門,咱們這教程的目標(biāo)是為了盡量降低門檻施逾,還是用core吧)
shell層是我鏈接核心層和用戶的地方,你可以簡(jiǎn)單理解為解析用戶輸入汉额,包裝核心層的計(jì)算結(jié)果曹仗,變成用戶看到的輸出闷愤。Shell層還有一個(gè)作用件余,當(dāng)我們有多個(gè)應(yīng)用的時(shí)候讥脐,彼此之間的shell層是互相交互的啼器,Core層是互相不知道彼此的存在的。
config層比較難理解端壳,從工程的角度告丢,我們的Core層和Shell層损谦,都不應(yīng)該控制彼此的生命周期,它們所有的依賴都應(yīng)該是外部配置的照捡,它們只依賴抽象的接口而不是具體實(shí)現(xiàn)颅湘。config層就是這一層配置栗精,在Linux的Shell里對(duì)應(yīng)的就是環(huán)境變量這個(gè)概念。
引入這些概念有什么好處呢悲立?如果用這些概念來解釋我們的應(yīng)用鹿寨,可以支撐非常大的架構(gòu)的思考薪夕。
我們想象一下,系統(tǒng)隨著演進(jìn)而變大原献,出現(xiàn)原本的小東西也會(huì)變的非常龐大馏慨。比如上一篇main函數(shù)里的幾行代碼,隨著系統(tǒng)變大熏纯,相應(yīng)的Router也不可能自己寫了,Router和Command的關(guān)系自然也是在文件里配置的樟澜,慢慢的我們就開始需要一個(gè)IOC容器误窖。Service本身會(huì)變得很復(fù)雜秩贰,彼此之間可能會(huì)有關(guān)系,而且甚至可能是別的應(yīng)用提供的Service毒费。如果我們?cè)偈褂脩?yīng)用框架里的概念丙唧,那么思考也好觅玻,交流也好,都會(huì)變得很低效溪厘。所以我們才引出了config胡本,shell畸悬,core這三個(gè)概念來簡(jiǎn)化應(yīng)用的內(nèi)部,這樣就可以思考大量的應(yīng)用之間的關(guān)系是應(yīng)該是一個(gè)什么樣的架構(gòu)了蹋宦,不過這個(gè)方向是一個(gè)更復(fù)雜的問題披粟,這里就不展開了冷冗。
回到我們的系統(tǒng)上,在一個(gè)剛剛出現(xiàn)了前后端概念的系統(tǒng)里贾惦,我們的新概念們能幫助我們理解架構(gòu)的演進(jìn),下面就基于這些概念帶著大家推演一遍現(xiàn)代常用的一些軟件框架是因?yàn)槟男┝α框?qū)動(dòng)出來的须板。
Core的重新定義
當(dāng)我們把系統(tǒng)分為客戶端和服務(wù)端的時(shí)候碰镜,那么客戶端要做什么呢习瑰?服務(wù)端要做什么呢?
往往客戶端是需要更多的照顧用戶體驗(yàn),填平服務(wù)端的接口和用戶體驗(yàn)之間的溝壑柠横。
服務(wù)端則要保證數(shù)據(jù)讀寫的性能窃款,安全性和易于被客戶端使用牍氛。
從這兩點(diǎn)來看,客戶端應(yīng)該考慮的是用戶體驗(yàn)搬俊,而不是讓用戶體驗(yàn)為服務(wù)端扭曲。所以客戶端的core層更多的是那層填平服務(wù)端接口和用戶體驗(yàn)之間溝壑的那堆代碼唉擂,而它給shell的接口應(yīng)該是以shell好調(diào)用為導(dǎo)向的餐屎。
那么怎么算是好用呢玩祟?
邊界與無限
剛才談到,在我們的邊界處空扎,好用是一個(gè)非常重要的需求藏鹊。怎么算好用呢勺卢?最重要的是邊界要找對(duì)象对,如果你過界了黑忱,做了事情也會(huì)被埋怨。當(dāng)我們思考系統(tǒng)的時(shí)候甫煞,邊界往往是不好找的。這就需要引入一個(gè)新的架構(gòu)模式: MVC冠绢。
所謂的MVC就是Model-View-Controller。一個(gè)常見系統(tǒng)弟胀,往往Model層負(fù)責(zé)核心基本元素和基本算法楷力,View層負(fù)責(zé)展示和表達(dá)數(shù)據(jù)孵户,Controller層負(fù)責(zé)調(diào)度和組合,把Model層和View層連接在一起夏哭。
一般來講,Model層通常是我們的Core竖配,Controller層往往就是我們的shell何址,View層就是shell返回的數(shù)據(jù)。這個(gè)時(shí)候邊界的思考就清楚了用爪,誰是我們的model和model相關(guān)的核心計(jì)算原押,誰就是我們的core偎血。誰是我們的Controller負(fù)責(zé)調(diào)度組合和內(nèi)外相連,誰就是我們的shell烁巫。而我們的計(jì)算結(jié)果署隘,也就是我們的View層亚隙,是會(huì)離開我們的應(yīng)用供別人使用的,那它是我們的最外面的邊界阿弃。我們思考邊界只需要關(guān)注在View層诊霹,思考清楚我們的View是否在一個(gè)抽象層次上就可以了渣淳。
但是,世界不是這么簡(jiǎn)單的分三層就可以結(jié)束了入愧,世界是往復(fù)循環(huán)以致無窮的鄙漏。所以我們的MVC也是循環(huán)迭代的棺蛛,也就是說MVC中的某一層還可能再分MVC。那么是哪一層呢旁赊?View層桦踊。還記得我們第一篇講得终畅,數(shù)據(jù)和過程是不嚴(yán)格區(qū)分的,所以我們返回的數(shù)據(jù)离福,可以被解析為新的過程杖狼,新的過程再產(chǎn)生新的數(shù)據(jù)术徊,從而往復(fù)循環(huán)以致無窮。
所以可以認(rèn)為服務(wù)端是原應(yīng)用的Core層進(jìn)化出來的。
如果我們把View層進(jìn)化下去子寓,我們前面提到的客戶端的Shell返回的數(shù)據(jù),還會(huì)再劃分斜友,就會(huì)有新的組件層(Component)炸裆,模版層(Layout)鲜屏,頁面層(Page,也有人愛用Container)等等洛史。
隨著出現(xiàn)了MVC惯殊,再進(jìn)一步思考就會(huì)逐漸發(fā)現(xiàn)也殖,其實(shí)core層不應(yīng)該關(guān)心后端代碼,它應(yīng)該關(guān)心前端的領(lǐng)域?qū)ο笠涫龋杂脩粞壑械哪P蜑榛A(chǔ)計(jì)算己儒,而不是后端業(yè)務(wù)人員眼中的模型為基礎(chǔ)來計(jì)算捆毫。所以他會(huì)把彌合后端和前端的工作交給shell層,而shell層會(huì)夾在三方面很難受绩卤,他一方面要對(duì)接后端途样,一方面要對(duì)接core省艳,還有一方面要適配真正的前端模型。前端和后端的拉鋸戰(zhàn)就會(huì)出現(xiàn)跋炕,在這股力量的擠壓之下,我們的BFF就會(huì)自然而然的出現(xiàn)律适,所謂的前后端分離也就是自然而然的事情了。
題外話
題外話1
對(duì)于有經(jīng)驗(yàn)的同學(xué)要說一句:數(shù)據(jù)庫不是核心捂贿,你的代碼才是核心。數(shù)據(jù)庫就是系統(tǒng)的另外一個(gè)應(yīng)用而已厂僧,雖然數(shù)據(jù)庫廠商希望你把核心放在它里面扣草,但你不要被廠商的策略綁架了你的自由。
題外話2
面向?qū)ο蟪矫睿幸晃煌瑢W(xué)總是在給我糾結(jié)這個(gè)面向?qū)ο笤趺串嫛F鋵?shí)之所以有此問并不是不知道怎么用強(qiáng)類型語言來畫圖密浑,如我們第二篇所述蛙婴,你換成類圖也是一樣的尔破。主要的糾結(jié)點(diǎn)在于,那個(gè)函數(shù)放在哪個(gè)類里這個(gè)問題懒构。
之所以一直沒講餐济,是因?yàn)槊嫦驅(qū)ο笫羌兇獾娜祟愃伎紗栴}的方式胆剧,它是非常不精確的,把哪個(gè)函數(shù)放在哪個(gè)類里這個(gè)事情是一門藝術(shù)而不是一門科學(xué)赞赖。當(dāng)我們有一個(gè)Dog類的時(shí)候滚朵,有一個(gè)bark方法是非常明顯的前域。當(dāng)我們有一個(gè)貨物類(Goods)的時(shí)候,算稅應(yīng)該是它的方法嗎匿垄?當(dāng)我們有一個(gè)數(shù)據(jù)庫實(shí)體的時(shí)候移宅,比如User椿疗,Item等等,那么存儲(chǔ)算他的方法嗎届榄?如果我們做一個(gè)CRM系統(tǒng)浅乔,Client明顯跟所有的業(yè)務(wù)都有關(guān)系铝条,總不能Client這個(gè)類上有所有業(yè)務(wù)的方法吧?所以我們無論怎么放班缰,都可能是有問題的贤壁,而且在變化來臨前埠忘,我們沒有什么客觀的標(biāo)準(zhǔn)來判斷當(dāng)前的做法是否合理馒索。
如果只是畫圖來表達(dá)的話,其實(shí)我們的方塊上名船,也表達(dá)出了哪個(gè)函數(shù)屬于哪個(gè)類,這樣已經(jīng)便于有經(jīng)驗(yàn)的人發(fā)現(xiàn)問題并解決問題了包帚。只是沒有任何客觀的公式可以幫助大家輕松的解決問題渔期。我們只能幫你這么多了渴邦,畢竟方法,從來也不是為弱者服務(wù)的谋梭。
題外話3
你這個(gè)是不是六邊形架構(gòu)信峻?
其實(shí)可以看作是六邊形架構(gòu)的一種變種瓮床,我只是比較討厭六邊形這個(gè)詞,它太容易讓人糾結(jié)為啥是六個(gè)邊隘庄,不是八個(gè)踢步?比如我叫八卦可不可以丑掺?所以我們也不要太糾結(jié)他叫什么,領(lǐng)會(huì)精神就好街州。