原創(chuàng): 張衛(wèi)濱 譯
Jean-Jacques Dubray是一名資深工程師,他最近引入了一個新的模式:狀態(tài)-行為-模(State-Action-Model,SAM)。SAM是一個函數(shù)式反應(yīng)型的編程模式,它致力于簡化數(shù)據(jù)Model和View之間的交互。它究竟有何優(yōu)點值得作者棄用MVC呢?
話題起因
在我最近的工作中,最讓人抓狂的就是為前端開發(fā)人員設(shè)計API。我們之間的對話大致就是這樣的:
開發(fā)人員:這個頁面上有數(shù)據(jù)元素x,y,z…,你能不能為我創(chuàng)建一個API,響應(yīng)格式為{x: , y:, z: }我:好吧
我甚至沒有進行進一步的爭論。項目結(jié)束時會積累大量的API,這些API與經(jīng)常發(fā)生變化的頁面是關(guān)聯(lián)在一起的,按照“設(shè)計”,只要頁面改變,相應(yīng)的API也要隨之變化,而在此之前,我們甚至對此毫不知情,最終,由于形成因素眾多且各平臺之間存在些許差異,必須創(chuàng)建非常多的API來滿足這些需求。
Sam Newman甚至將這種制度化的過程稱之為BFF模式,這種模式建議為每種設(shè)備、平臺當然還包含App版本開發(fā)特定的API。 Daniel Jacobson在接受InfoQ的采訪時曾指出,Netflix頗為勉強地將“體驗式API”與“臨時API(Ephemeral API)”劃上了等號。 唉……
幾個月前,我開始思考是什么造成了如今的這種現(xiàn)象,該做些什么來應(yīng)對它,這個過程使我開始質(zhì)疑應(yīng)用架構(gòu)中最強大的理念,也就是MVC,我感受到了函數(shù)式反應(yīng)型編程(reactive)的強大威力,這個過程致力于流程的簡化,并試圖消除我們這個行業(yè)在生產(chǎn)率方面的膨脹情緒。我相信你會對我的發(fā)現(xiàn)感興趣的。
MVC的輝煌過去與現(xiàn)存問題
在每個用戶界面背后,我們都在使用MVC模式,也就是模型-視圖-控制器(Model-View-Controller)。MVC發(fā)明的時候,Web尚不存在,當時的軟件架構(gòu)充其量是胖客戶端在原始網(wǎng)絡(luò)中直接與單一數(shù)據(jù)庫會話。但是,幾十年之后,MVC依然在使用,持續(xù)地用于OmniChannel應(yīng)用的構(gòu)建。
Angular 2正式版即將發(fā)布,在這個時間節(jié)點重估MVC模式及各種MVC框架為應(yīng)用架構(gòu)帶來的貢獻意義重大。
我第一次接觸到MVC是在1990年,當時NeXT剛剛發(fā)布Interface Builder(讓人驚訝的是,如今這款軟件依然發(fā)揮著重大的作用)。當時,我們感覺Interface Builder和MVC是一個很大的進步。在90年代末期,MVC模式用到了HTTP上的任務(wù)中(還記得Struts嗎?),如今,就各個方面來講,MVC是所有應(yīng)用架構(gòu)的基本原則。
MVC的影響十分深遠,以致于React.js在介紹他們的框架時都委婉地與其劃清界限:“React實現(xiàn)的只是MVC中視圖(View)的部分”。
當我去年開始使用React的時候,我感覺它在某些地方有著明顯的不同:你在某個地方修改一部分數(shù)據(jù),不需要顯式地與View和Model進行交互,整個UI就能瞬間發(fā)生變化(不僅僅是域和表格中的值)。這也就是說,我很快就對React的編程模型感到了失望,在這方面,我顯然并不孤獨。我分享一下Andre Medeiros的觀點:
React在很多方面都讓我感到失望,它主要是通過設(shè)計不佳的API來引導(dǎo)程序員[…]將多項關(guān)注點混合到一個組件之中。
作為服務(wù)端的API設(shè)計者,我的結(jié)論是沒有特別好的方式將API調(diào)用組織到React前端中,這恰恰是因為React只關(guān)注View,在它的編程模型中根本不存在控制器。
到目前為止,F(xiàn)acebook一直致力于在框架層面彌合這一空白。React團隊起初引入了Flux模式,不過它依然令人失望,最近Dan Abramov又提倡另外一種模式,名為Redux,在一定程度上來講,它的方向是正確的,但是在將API關(guān)聯(lián)到前端方面,依然比不上我下面所介紹的方案。
google發(fā)布過GWT、Android SDK還有Angular,你可能認為他們的工程師熟知何為最好的前端架構(gòu),但是當你閱讀Angular 2設(shè)計考量的文章時,便會不以為然,即便在Google大家也達成這樣的共識,他們是這樣評價之前的工作成果的:
Angular 1并不是基于組件的理念構(gòu)建的。相反,我們需要將控制器與頁面上各種[元素]進行關(guān)聯(lián)(attach),其中包含了我們的自定義邏輯。根據(jù)我們自定義的指令如何對其進行封裝(是否包含isolate scope?),scope會進行關(guān)聯(lián)或繼續(xù)往下傳遞。
基于組件的Angular 2看起來能簡單一點嗎?其實并沒有好多少。Angular 2的核心包本身就包含了180個語義(semantics),整個框架的語義已經(jīng)接近500個,這是基于html5和css3的。誰有那么多時間學(xué)習(xí)和掌握這樣的框架來構(gòu)建Web應(yīng)用呢?當Angular 3出現(xiàn)的時候,情況又該是什么樣子呢?
在使用過React并了解了Angular 2將會是什么樣子之后,我感到有些沮喪:這些框架都系統(tǒng)性地強制我使用BFF“頁面可替換模式(Screen Scraping)”模式,按照這種模式,每個服務(wù)端的API要匹配頁面上的數(shù)據(jù)集,不管是輸入的還是輸出的。
棄用MVC之后怎么走?
此時,我決定“讓這一切見鬼去吧”。我構(gòu)建了一個Web應(yīng)用,沒有使用React、沒有使用Angular也沒有使用任何其他的MVC框架,通過這種方式,我看一下是否能夠找到一種在View和底層API之間進行更好協(xié)作的方式。
就React來講,我最喜歡的一點在于Model和View之間的關(guān)聯(lián)關(guān)系。React不是基于模板的,View本身沒有辦法請求數(shù)據(jù)(我們只能將數(shù)據(jù)傳遞給View),看起來,針對這一點進行探索是一個很好的方向。
如果看得足夠長遠的話,你會發(fā)現(xiàn)React唯一的目的就是將View分解為一系列(純粹的)函數(shù)和JSX語法:
<V params={M}/>
它實際上與下面的格式并沒有什么差別:
V = f( M )
例如,我當前正在從事項目的Web站點, Gliiph,就是使用這種函數(shù)構(gòu)建的:

圖1:用于生成站點Slider組件HTML的函數(shù)
這個函數(shù)需要使用Model來填充數(shù)據(jù):

圖2:支撐slider的Model
如果用簡單的JAVAScript函數(shù)就能完成任務(wù),我們?yōu)槭裁催€要用React呢?
虛擬DOM(virtual-dom)?如果你覺得需要這樣一種方案的話(我并不確定有很多的人需要這樣),其實有這樣的可選方案,我也期望開發(fā)出更多的方案。
GraphQL?并不完全如此。不要因為Facebook大量使用它就對其產(chǎn)生誤解,認為它一定是對你有好處的。GraphQL僅僅是以聲明的方式來創(chuàng)建視圖模型。強制要求Model匹配View會給你帶來麻煩,而不是解決方案。React團隊可能會覺得使用“客戶端指定查詢(Client-specified queries)”是沒有問題的(就像反應(yīng)型團隊中那樣):
GraphQL完全是由View以及編寫它們的前端工程師的需求所驅(qū)動的。[…]另一方面,GraphQL查詢會精確返回客戶端請求的內(nèi)容,除此之外,也就沒什么了。
GraphQL團隊沒有關(guān)注到JSX語法背后的核心思想:用函數(shù)將Model與View分離。與模板和“前端工程師所編寫的查詢”不同,函數(shù)不需要Model來適配View。
當View是由函數(shù)創(chuàng)建的時候(而不是由模板或查詢所創(chuàng)建),我們就可以按需轉(zhuǎn)換Model,使其按照最合適的形式來展現(xiàn)View,不必在Model的形式上添加人為的限制。
例如,如果View要展現(xiàn)一個值v,有一個圖形化的指示器會標明這個值是優(yōu)秀、良好還是很差,我們沒有理由將指示器的值放到Model中:函數(shù)應(yīng)該根據(jù)Model所提供的v值,來進行簡單的計算,從而確定指示器的值。
現(xiàn)在,把這些計算直接嵌入到View中并不是什么好主意,使View-Model成為一個純函數(shù)也并非難事,因此當我們需要明確的View-Model時,就沒有特殊的理由再使用GraphQL了:
V = f( vm(M) )
作為深諳MDE之道的人,我相信你更善于編寫代碼,而不是元數(shù)據(jù),不管它是模板還是像GraphQL這樣的復(fù)雜查詢語言。
這個函數(shù)式的方式能夠帶來多項好處。首先,與React類似,它允許我們將View分解為組件。它們創(chuàng)建的較為自然的界面允許我們?yōu)閃eb應(yīng)用或Web站點設(shè)置“主題”,或者使用不同的技術(shù)來渲染View(如原生的方式)。函數(shù)實現(xiàn)還有可能增強我們實現(xiàn)反應(yīng)型設(shè)計的方式。
在接下來的幾個月中,可能會出現(xiàn)開發(fā)者交付用JavaScript函數(shù)包裝的基于組件的HTML5主題的情況。這也是最近這段時間,在我的Web站點項目中,我所采用的方式,我會得到一個模板,然后迅速地將其封裝為JavaScript函數(shù)。我不再使用wordPress/ target=_blank class=infotextkey>WordPress。基本上花同等的工夫(甚至更少),我就能實現(xiàn)HTML5和CSS的最佳效果。
這種方式也需要在設(shè)計師和開發(fā)人員之間建立一種新型的關(guān)系。任何人都可以編寫這些JavaScript函數(shù),尤其是模板的設(shè)計人員。人們不需要學(xué)習(xí)綁定方法、JSX和Angular模板的語法,只掌握簡單的JavaScript核心函數(shù)就足以讓這一切運轉(zhuǎn)起來。
有意思的是,從反應(yīng)型流程的角度來說,這些函數(shù)可以部署在最合適的地方:在服務(wù)端或在客戶端均可。
但最為重要的是,這種方式允許在View與Model之間建立最小的契約關(guān)系,讓Model來決定如何以最好的方式將其數(shù)據(jù)傳遞給View。讓Model去處理諸如緩存、懶加載、編配以及一致性的問題。與模板和GraphQL不同,這種方式不需要從View的角度來直接發(fā)送請求。
既然我們有了一種方式將Model與View進行解耦,那么下一個問題就是:在這里該如何創(chuàng)建完整的應(yīng)用模型呢?“控制器”該是什么樣子的?為了回答這個問題,讓我們重新回到MVC上來。
蘋果公司了解MVC的基本情況,因為他們在上世紀80年代初,從Xerox PARC“偷來了”這一模式,從那時起,他們就堅定地實現(xiàn)這一模式:

圖3:MVC模式
Andre Medeiros曾經(jīng)清晰地指出,這里核心的缺點在于, MVC模式是“交互式的(interactive)”(這與反應(yīng)型截然不同)。在傳統(tǒng)的MVC之中,Action(Controller)將會調(diào)用Model上的更新方法,在成功(或出錯)之時會確定如何更新View。他指出,其實并非必須如此,這里還有另外一種有效的、反應(yīng)型的處理方式,我們只需這樣考慮,Action只應(yīng)該將值傳遞給Model,不管輸出是什么,也不必確定Model該如何進行更新。
那核心問題就變成了:該如何將Action集成到反應(yīng)型流程中呢?如果你想理解Action的基礎(chǔ)知識的話,那么你應(yīng)該看一下TLA+。TLA代表的是“Action中的邏輯時序(Temporal Logic of Actions)”,這是由Dr. Lamport所提出的學(xué)說,他也因此獲得了圖靈獎。在TLA+中,Action是純函數(shù):
data’ = A (data)
我真的非常喜歡TLA+這個很棒的理念,因為它強制函數(shù)只轉(zhuǎn)換給定的數(shù)據(jù)集。
按照這種形式,反應(yīng)型MVC看起來可能就會如下所示:
V = f( M.present( A(data) ) )
這個表達式規(guī)定當Action觸發(fā)的時候,它會根據(jù)一組輸入(例如用戶輸入)計算一個數(shù)據(jù)集,這個數(shù)據(jù)是提交到Model中的,然后會確定是否需要以及如何對其自身進行更新。當更新完成后,View會根據(jù)新的Model狀態(tài)進行更新。反應(yīng)型的環(huán)就閉合了。Model持久化和獲取其數(shù)據(jù)的方式是與反應(yīng)型流程無關(guān)的,所以,它理所應(yīng)當?shù)?ldquo;不應(yīng)該由前端工程師來編寫”。不必因此而感到歉意。
再次強調(diào),Action是純函數(shù),沒有狀態(tài)和其他的副作用(例如,對于Model,不會包含計數(shù)的日志)。
反應(yīng)型MVC模式很有意思,因為除了Model以外,所有的事情都是純函數(shù)。公平來講,Redux實現(xiàn)了這種特殊的模式,但是帶有React不必要的形式,并且在reducer中,Model和Action之間存在一點不必要的耦合。Action和接口之間是純粹的消息傳遞。
這也就是說,反應(yīng)型MVC并不完整,按照Dan喜歡的說法,它并沒有擴展到現(xiàn)實的應(yīng)用之中。讓我們通過一個簡單的樣例來闡述這是為什么。
假設(shè)我們需要實現(xiàn)一個應(yīng)用來控制火箭的發(fā)射:一旦我們開始倒計時,系統(tǒng)將會遞減計數(shù)器(counter),當它到達零的時候,會將Model中所有未定的狀態(tài)設(shè)置為規(guī)定值,火箭的發(fā)射將會進行初始化。

這個應(yīng)用有一個簡單的狀態(tài)機:

圖4:火箭發(fā)射的狀態(tài)機
其中decrement和launch都是“自動”的Action,這意味著我們每次進入(或重新進入)counting狀態(tài)時,將會保證進行轉(zhuǎn)換的評估,如果計數(shù)器的值大于零的話,decrement Action將會繼續(xù)調(diào)用,如果值為零的話,將會調(diào)用launchAction。在任何的時間點都可以觸發(fā)abort Action,這樣的話,控制系統(tǒng)將會轉(zhuǎn)換到aborted狀態(tài)。
在MVC中,這種類型的邏輯將會在控制器中實現(xiàn),并且可能會由View中的一個計時器來觸發(fā)。
這一段至關(guān)重要,所以請仔細閱讀。我們已經(jīng)看到,在TLA+中,Action沒有副作用,只是計算結(jié)果的狀態(tài),Model處理Action的輸出并對其自身進行更新。這是與傳統(tǒng)狀態(tài)機語義的基本區(qū)別,在傳統(tǒng)的狀態(tài)機中,Action會指定結(jié)果狀態(tài),也就是說,結(jié)果狀態(tài)是獨立于Model的。
在TLA+中,所啟用的Action能夠在狀態(tài)表述(也就是View)中進行觸發(fā),這些Action不會直接與觸發(fā)狀態(tài)轉(zhuǎn)換的行為進行關(guān)聯(lián)。換句話說,狀態(tài)機不應(yīng)該由連接兩個狀態(tài)的元組(S1, A, S2)來進行指定,傳統(tǒng)的狀態(tài)機是這樣做的,它們元組的形式應(yīng)該是(Sk, Ak1, Ak2,…),這指定了所有啟用的Action,并給定了一個狀態(tài)Sk,Action應(yīng)用于系統(tǒng)之后,將會計算出結(jié)果狀態(tài),Model將會處理更新。
當我們引入“state”對象時,TLA+提供了一種更優(yōu)秀的方式來對系統(tǒng)進行概念化,它將Action和view(僅僅是一種狀態(tài)的表述)進行了分離。
我們樣例中的Model如下所示:
model = {
counter: ,
started: ,
aborted: ,
launched:
}
系統(tǒng)中四個(控制)狀態(tài)分別對應(yīng)于Model中如下的值:
ready = {counter: 10, started: false, aborted: false, launched: false }
counting = {counter: [0..10], started: true, aborted: false, launched: false }
launched = {counter: 0, started: true, aborted: false, launched: true}
aborted = {counter: [0..10], started: true, aborted: true, launched: false}
這個Model是由系統(tǒng)的所有屬性及其可能的值所指定的,狀態(tài)則指定了所啟用的Action,它會給定一組值。這種類型的業(yè)務(wù)邏輯必須要在某個地方進行實現(xiàn)。我們不能指望用戶能夠知道哪個Action是否可行。在這方面,沒有其他的方式。不過,這種類型的業(yè)務(wù)邏輯很難編寫、調(diào)試和維護,在沒有語義對其進行描述時,更是如此,比如在MVC中就是這樣。
讓我們?yōu)榛鸺l(fā)射的樣例編寫一些代碼。從TLA+角度來講,next-action斷言在邏輯上會跟在狀態(tài)渲染之后。當前狀態(tài)呈現(xiàn)之后,下一步就是執(zhí)行next-action斷言,如果存在的話,將會計算并執(zhí)行下一個Action,這個Action會將其數(shù)據(jù)交給Model,Model將會初始化新狀態(tài)的表述,以此類推。

圖5:火箭發(fā)射器的實現(xiàn)
需要注意的是,在客戶端/服務(wù)器架構(gòu)下,當自動Action觸發(fā)之后,我們可能需要使用像WebSocket這樣的協(xié)議(或者在WebSocket不可用的時候,使用輪詢機制)來正確地渲染狀態(tài)表述。
我曾經(jīng)使用Java和JavaScript編寫過一個很輕量級的開源庫,它使用TLA+特有的語義來構(gòu)造狀態(tài)對象,并提供了樣例,這些樣例使用WebSocket、輪詢和隊列實現(xiàn)瀏覽器/服務(wù)器交互。在火箭發(fā)射器的樣例中可以看到,我們并非必須要使用那個庫。一旦理解了如何編寫,狀態(tài)實現(xiàn)的編碼相對來講是很容易的。
新模式——SAM模式
對于要引入的新模式來說,我相信我們已經(jīng)具備了所有的元素,這個新模式作為MVC的替代者,名為SAM模式(狀態(tài)-行為-模型,State-Action-Model),它具有反應(yīng)型和函數(shù)式的特性,靈感來源于React.js和TLA+。
SAM模式可以通過如下的表達式來進行描述:
V = S( vm( M.present( A(data) ) ), nap(M))
它表明在應(yīng)用一個Action A之后,View V可以計算得出,Action會作為Model的純函數(shù)。
在SAM中,A(Action)、vm(視圖-模型,view-model)、nap(next-action斷言)以及S(狀態(tài)表述)必須都是純函數(shù)。在SAM中,我們通常所說的“狀態(tài)”(系統(tǒng)中屬性的值)要完全局限于Model之中,改變這些值的邏輯在Model本身之外是不可見的。
隨便提一下,next-action斷言,即nap()是一個回調(diào),它會在狀態(tài)表述創(chuàng)建完成,并渲染給用戶時調(diào)用。

圖6:狀態(tài)-行為-模型(SAM)模式
模式本身是獨立于任何協(xié)議的(可以不費什么力氣就能在HTTP上實現(xiàn))和客戶端/服務(wù)器拓撲結(jié)構(gòu)的。
SAM并不意味著我們必須要使用狀態(tài)機的語義來獲取View的內(nèi)容。如果Action是由View觸發(fā)的,那next-action斷言就是一個空函數(shù)。不過,這可能是一個很好的實踐,它清晰暴露了底層狀態(tài)機的控制狀態(tài),因為根據(jù)(控制)狀態(tài)的不同,View看起來可能也是不同的。
另一方面,如果你的狀態(tài)機涉及到自動化的Action,那么Action和Model都不可能做到純粹的不包含next-action斷言:有些Action將會變得有狀態(tài),或者Model必須要觸發(fā)Action,而這本來并不是它的角色。順便提一下,也許并不那么直觀,狀態(tài)對象并沒有持有任何的“狀態(tài)”,它同樣也是純函數(shù),它會渲染View并計算next-action斷言,這兩者都來源于Model的屬性值。
這種新模式的好處在于,它清晰地將CRUD操作從Action中分離了出來。Model負責它的持久化,將會通過CRUD操作來實現(xiàn),通過View是無法進行訪問的。尤其是,View永遠不會處于“獲取”數(shù)據(jù)的位置,View所能做的唯一的事情就是請求系統(tǒng)中當前的狀態(tài)表述并通過觸發(fā)Action初始化一個反應(yīng)型流程。
Action僅僅代表了一種具有權(quán)限的通道,以此來建議Model該怎樣進行變更。它們本身(在Model方面)并沒有什么副作用。如果必要的話,Action會調(diào)用第三方的API(同樣,對Model沒有副作用),比如說,修改地址的Action可能會希望調(diào)用地址校驗服務(wù),并將服務(wù)返回的地址提交到Model中。
如下就是“修改地址”Action該如何進行實現(xiàn),它會調(diào)用地址校驗的API:

圖7:“修改地址”的實現(xiàn)
模式中的元素,包括Action和Model,可以進行自由地組合:
函數(shù)組合
data’ = A(B(data))
端組合(Peer)(相同的數(shù)據(jù)集可以提交給兩個Model)
M1.present(data’)
M2.present(data’)
父子組合(父Model控制的數(shù)據(jù)集提交給子Model)
M1.present(data’,M2)
function present(data, child) {
// 執(zhí)行更新
…
// 同步Model
child.present(c(data))
}
發(fā)布/訂閱組合
M1.on(“topic”, present )
M2.on(“topic”, present )
或
M1.on(“data”, present )
M2.on(“data”, present )
有些架構(gòu)師可能會考慮到System of Record和Systems of Engagement,這種模式有助于明確這兩層的接口(圖8),Model會負責與systems of record的交互。

圖8:SAM組合模型
整個模式本身也是可以進行組合的,我們可以實現(xiàn)運行在瀏覽器中的SAM實例,使其支持類似于向?qū)В╳izard)的行為(如ToDo應(yīng)用),它會與服務(wù)器端的SAM進行交互:

圖9:SAM實例組合
請注意,里層的SAM實例是作為狀態(tài)表述的一部分進行傳送的,這個狀態(tài)表述是由外層的實例所生成的。
會話檢查應(yīng)該在Action觸發(fā)之前進行(圖10)。SAM能夠啟用一項很有意思的組合,在將數(shù)據(jù)提交給Model之前,View可以調(diào)用一個第三方的Action,并且要為其提供一個token和指向系統(tǒng)Action的回調(diào),這個第三方Action會進行授權(quán)并校驗該調(diào)用的合法性。

圖10:借助SAM實現(xiàn)會話管理
從CQRS的角度來講,這個模式?jīng)]有對查詢(Query)和命令(Command)做特殊的區(qū)分,但是底層的實現(xiàn)需要進行這種區(qū)分。搜索或查詢“Action”只是簡單地傳遞一組參數(shù)到Model中。我們可以采用某種約定(如下劃線前綴)來區(qū)分查詢和命令,或者我們可以在Model上使用兩個不同的present方法:
{ _name : ‘/^[a]$/i’ } // 名字以A或a開頭 { _customerId: ‘123’ } // id=123的customer
Model將會執(zhí)行必要的操作以匹配查詢,更新其內(nèi)容并觸發(fā)View的渲染。類似的約定可以用于創(chuàng)建、更新或刪除Model中的元素。在將Action的輸出傳遞給Model方面,我們可以實現(xiàn)多種方式(數(shù)據(jù)集、事件、Action……)。每種方式都會有其優(yōu)勢和不足,最終這取決于個人偏好。我更喜歡數(shù)據(jù)集的方式。
在異常方面,與React類似,我們預(yù)期Model會以屬性值的形式保存異常信息(這些屬性值可能是由Action提交的,也可能是CRUD操作返回的)。在渲染狀態(tài)表述的時候,會用到屬性值,以展現(xiàn)異常信息。
在緩存方面,SAM在狀態(tài)表述層提供了緩存的選項。直觀上來看,緩存這些狀態(tài)表述函數(shù)的結(jié)果能夠?qū)崿F(xiàn)更高的命中率,因為我們現(xiàn)在是在組件/狀態(tài)層觸發(fā)緩存,而不是在Action/響應(yīng)層。
該模式的反應(yīng)型和函數(shù)式結(jié)構(gòu)使得功能重放(replay)和單元測試變得非常容易。
SAM模式完全改變了前端架構(gòu)的范式,因為根據(jù)TLA+的基礎(chǔ)理念,業(yè)務(wù)邏輯可以清晰地描述為:
- Action是純函數(shù)
- CRUD操作放在Model中
- 狀態(tài)控制自動化的Action
作為API的設(shè)計者,從我的角度來講,這種模式將API設(shè)計的責任推到了服務(wù)器端,在View和Model之間保持了最小的契約。
Action作為純函數(shù),能夠跨Model重用,只要某個Model能夠接受Action所對應(yīng)的輸出即可。我們可以期望Action庫、主題(狀態(tài)表述)甚至Model能夠繁榮發(fā)展起來,因為它們現(xiàn)在能夠獨立地進行組合。
借助SAM模式,微服務(wù)能夠非常自然地支撐Model。像Hivepod.io這樣的框架能夠插入進來,就像它本來就在這層似得。
最為重要的是,這種模式像React一樣,不需要任何的數(shù)據(jù)綁定或模板。
隨著時間的推移,我希望能夠推動瀏覽器永久添加虛擬DOM的特性,新的狀態(tài)表述能夠通過專有API直接進行處理。
我發(fā)現(xiàn)這個旅程將會帶來一定的革新性:在過去的幾十年中,面向?qū)ο笏坪鯚o處不在,但它已經(jīng)一去不返了。我現(xiàn)在只能按照反應(yīng)型和函數(shù)式來進行思考。我借助SAM所構(gòu)建的東西及其構(gòu)建速度都是前所未有的。另外,我能夠關(guān)注于API和服務(wù)的設(shè)計,它們不再遵循由前端決定的模式。