這幾天在知乎看到了許多關於濫用三層的討論,很多觀點都暗指三層架構這東西太Low了。Low嗎?其實不然。我們覺得三層Low主要是因為三層太簡單了,人們往往不理解三層的思想和目的,濫用三層,加之一些劣質程式員為其加上些自認牛逼的更改搞個五六七八層,代碼寫得又爛,結果看起來就很Low,於是人們就把鍋扣到三 ...
這幾天在知乎看到了許多關於濫用三層的討論,很多觀點都暗指三層架構這東西太Low了。Low嗎?其實不然。我們覺得三層Low主要是因為三層太簡單了,人們往往不理解三層的思想和目的,濫用三層,加之一些劣質程式員為其加上些自認牛逼的更改搞個五六七八層,代碼寫得又爛,結果看起來就很Low,於是人們就把鍋扣到三層頭上。
那麼三層的目的是什麼,意義在哪裡呢?其實三層的出發點是“關註點分離”,三層是“關註點分離”思想的一種實現或者說體現。許多用三層的人往往不理解這一點,甚至為了讓項目看起來高大上,還要再多加幾層,再加個Bll介面、Dal介面什麼的,美其名曰N層架構,看到這我想大家都要吐了,太Low了,簡直是太Low。人們已經忘記了為什麼要用三層了,思想已被束縛住。
那麼怎麼改?不用三層了嗎?那我們用什麼?業務和數據訪問寫一起嗎?當然不,現在三層被唾棄,主要原因是因為三層還是十年前(這隻是個比喻)那個三層,十年和現在寫的代碼和寫代碼的方式變化巨大,用十年前的辦法解決現在的問題,顯然有bug。
技術日新月異,那年的三層是為瞭解決代碼太多,太混亂,現在呢,前後端分離,數據訪問有ORM,代碼沒那麼亂了,我們的架構也應該跟著改進。比如你的項目里用的EF那麼就可以考慮去掉數據訪問層,因為EF大大減少了數據訪問的代碼(我看eShopOnContainers里就是),以前的一個方法,用EF可能只有一行而已。而我現在用Dapper,代碼量還是有的,因為SQL格式化占行很多,都寫在邏輯里還是不合適。
大家可以看看微軟的eshorpOnContainers項目的代碼,裡面沒有分層,但是也是按照“關註點分離”的方式分了文件夾,也可以說它只是分了兩層。的確,我們一定要保留層的概念嗎?其實不一定,想想我們過去的多層結構里有什麼層,表示層、業務邏輯層、數據訪問層、通用層、模型層。你看一個基本的多層架構就有5層,好吧,這些都是層嗎?仔細想想其實不是,通用層和模型成是貫穿的,不應該算作層。寫到這裡我去看了我過去寫的採用三層的項目,只看Common一層,我發現,這裡的方法、類都是特定的,特定用於某一層的,比如md5這個只有bll用的到,IsEmail等方法也只有bll用的到。所以我們可以把Common一層刪掉,屬於哪個層的代碼,放回到那一層。比如可以在Dal和Bll里各寫一個 internal 的Common(名字就隨便起啦)類用來包裝屬於這一層的通用代碼,internal保證不會和其它層衝突。我看eShop里也沒有什麼Common之類的項目,只是在一些項目里會有一些類,用來封裝這個程式集本身會用到的東西。
最近看了看IdentityServer4的源碼(很少量),我發現了它和微軟代碼的一些共同處,其中之一就是Services,這裡麵包含了業務邏輯的代碼。這就提示了我們:“是時候拋棄層的概念了”。是不是,其實N層里真正有層級關係的也只是3層而已,即使再加一個Cache層,比如Redis,那這個層是不是也應該只由Bll層來調用才合適呢,所以我們先拋棄層的概念,把原來層的概念拋棄,項目名字改一改,只用三層的基本概念“關註點分離”來設計項目的機構,那麼項目結構就不是層狀的了,而是塊狀的。
我們把三層的結構改一改:
這是一個含有Cache(比如可以用Redis)的架構,你看把Bll改成Service,Dal改成Store是不是看起來變得高大上了呢。
我來解讀一下這個架構設計: Store和Cache只由Service調用,表示層的各種程式負責收集數據、轉換數據、基本數據驗證(模型驗證),說到數據驗證,現在的Web應用的發展方向就是前後端分離,後端基本就只傳個數據,不再負責渲染,這種模型就像是App了,用戶瀏覽網頁其實是我分發個html客戶端給他。而且在驗證數據的時候,現在幾乎沒有不用前端驗證的吧,無論是web和app,那麼後端驗證數據就不用搞一個複雜的結果給前端,只要告訴前端“不對,數據不對”,說個大概就行了,因為正常用戶不會繞過前端驗證,不繞過前端驗證服務端驗證就不會有問題,所以使用簡單的消息告知前端有錯誤。那麼現在表現層的任務就簡單了:收集數據、基本驗證、轉換傳出數據。
Service模塊繼續承擔以前業務邏輯層的功能,搞搞業務邏輯,比如用戶要數據,Api先搞一搞,看看身份啊, Id啊什麼的有沒有什麼問題,然後Service先驗證關鍵數據(就是不是基本類型的,和邏輯緊密相關的,比如某個東西存不存在什麼的),然後去Cache搞一搞,沒有再到Store里搞一搞,最後搞點數據給後端Api,Api序列化之後再傳回去。
其實我們還是分層了,而且就是三層,服務端的三層,變化就是我們明晰了層的意義,一個層也不一定只裝一類代碼,分層的依據也變了,現在是調用關係主導分層,不再是過去的固定模式了。
你也可以這樣理解,整個程式只有兩層,一層是Api用於暴露給,遠端的Client,Service是一層,處理業務邏輯,Service用Store處理存儲在資料庫中的數據,用Cache處理緩存。而Model將它們聯繫在一起,使其成為一個monolithic程式(對,就是那個談微服務是談到那個monolithic),說到這裡又想說在eShopOnContainers項目里的微服務,那個Web程式在讀取數據的時候是去掉相應的Api通過Http去另一個App去拿取數據。需要Catalog就找CatalogApi要,需要什麼就找什麼Api去要。寫到這裡你應該能想到這篇文章一直在討論的真正的東西“關註點分離”,你可以多種角度去觀察一個項目/產品/網站,用不同的方式去歸類。
你看這個改進的三層架構,如果以模塊的角度看,它就不是三層了,去掉了哪些無用的東西,沒有定式,結構看起來還挺實用。
一些的架構設計或者其他框架(比如說最容易和三層混淆MVC)的設計都很像三層,因為他們都有體現關註點分離,而且電腦里離不開數據和界面顯示和邏輯。所以大家看起來都有三層的影子,或者說互相像互相,只是應用三層太廣泛了。
分層就像是橫切,微服務就像是縱切,複雜一點的就是交叉切,整個系統變成一個一個的微粒,難道我們編寫大型應用的最終形態就是微服務? 也許吧。可能當一個系統足夠大的時候,橫切分層就不太管用了,每一層的代碼量也變得巨多,團隊協作也難了。這時候拆分微服務就太恰當了,這幾天看到博客園裡某園有說到:“誰都不會用三層寫Hello world”(大意是這個),他說的對,我們設計架構的時候,要考慮我們系統的體量,太大了就得拆,最終就是拆成顆粒,基本上就是微服務了。但是不大就沒必要,微服務成本也是有的,只有系統大過某一程度(這個程度自己考量,據我觀察這個程度值並不是很高)的時候,微服務帶來的利益才會高出成本,微服務價值才能得到體現。上述中用模塊(塊、顆粒)的概念重新描繪整個架構,我斯認為這是一種像微服務架構靠攏的表現。
我看eShop的源碼量也很少,我想看看微軟是如何寫項目的,高級程式員的架構是如何設計的,能給我的開髮帶來什麼啟發,所,以希望大家也能去看看。
縱觀現有的各種框架 mvc mvp mvvm 還有什麼麽其它的,他們的設計方式似乎後符合“關註點分離”這一思想,那麼為什麼?那就得思考關註點分離能帶來什麼?分組開發,專人專事,擅長搞前端的去搞前端,擅長P圖的去P圖。提高每個人的效率,而又能讓整個系統完美的運轉起來,這就是我對於關註點分離的理解。
話說回來,三層真的用不得了嗎?不知道,我覺得改改還能用,先把“層”的概念放一放,三層的目的是關註點分離,我們可以直接從此來思考架構設計,而不必一定將架構設計成一層一層的。
鑒於我的理解,我在架構設計中,會關註適用性以及目的,對基本架構做出改變。加勒比海盜中有一句臺詞令我印象深刻:“法典只不過是一些指導,它並不是必須遵守的規定”。我們不能被框架/架構束縛住,普適性原則不是規定,它總是可以變的。
哦,最後我還建了個上面所設計改進版的項目,貼個圖。註:我還沒有實際應用過這個設計,如果你要參考,那麼請想好了再參考
歡迎大家加入.NetCore學習交流群 群號:180537383