看板 Soft_Job 關於我們 聯絡資訊
HI! 小弟最近對web back-end MVC框架有些想法 跟各位分享一下 <( _ _ )> 網頁好讀版 框架不應該有「MODELS」資料夾 http://blog.turn.tw/?p=1541 -------------------------------------------- 各大back-end framework幾乎都採用了「MVC」架構。 它們至少會有「views」、「 controllers」、「 models」三個資料夾。 「views」跟「controllers」沒太大問題,但是「models」資料夾根本不該存在。 我要對這些框架提出嚴厲指控: 「models」資料夾的存在是一種錯誤的架構設計。它不但阻礙新手學習,還會傷害 scalability。 任何back-end framework都不應該有「models」資料夾。 我會在這篇文章解釋理由,並且提出改善架構設計的幾個方向。 哪些框架有「models」資料夾? 光是我接觸過的Ruby、PHP框架,就至少有: Rails(Ruby) CodeIgniter(PHP) Yii(PHP) … 除此之外,像是Java, Python還是什麼語言, 一定也有框架做這種事:放一個「models」資料夾在那邊。 這真是大錯特錯。 你到底要放什麼東西到「models」資料夾裡面?你覺得Model是什麼? Model是什麼? 撇開我之前提到的MVC正名爭議不談,光是MVC的M該如何解釋就已經是個大哉問。 看看這則出色的Stack Overflow問答: How should a model be structured in MVC? Model是layer、它包羅萬象、它涵蓋你全部的business logic。 眾說紛紜中,這是我們唯一能有的共識。 Model難以定義、沒有絕對正確的架構設計。聽起來真令人洩氣,對吧? 不!這樣很好!這樣才對!軟體架構本來就是大哉問,有無限種可能的方法, 這也是我們所有人應該要一起討論和嘗試的地方。 而「models」資料夾卻嚴重妨礙我們討論、阻止我們思考, 它不但阻礙新手學習,還會傷害scalability。 「models」資料夾如何阻礙新手學習? 說明這段之前,我先定義一個名詞:「entity」。 我將entity定義成「代表現實生活中的一種事物」。 以常見的Active Record pattern來說, // user.rb // Rails class User < ActiveRecord::Base // user.php // Laravel class User extends Eloquent // post.php // Yii class Post extends CActiveRecord 類似這樣的東西,你一定看過。 user.rb、user.php、post.php,這些就是我所謂的「entity」。 也就是這些「entity」,讓新手容易誤以為「entity」就是MVC裡面的M。 錯!M是layer,entity只是M裡面的組成元素之一而已。 「models」資料夾的存在本身,會讓新手以為「弄幾個entity類別丟進去就搞定架構了」 然後entity的行為、對entity做出的行為、關乎兩個以上entity的行為, 管他什麼logic,管他什麼行為,全部想辦法塞進entity類別。 下場通常就是:那些entity類別最後變得超肥胖、難以理解、動輒達到上千行程式碼。 我稱之為「胖胖entity」。 「models」資料夾帶給新手「model == entity」錯覺! 「models」資料夾誘惑新手去做出一堆胖胖entity! 「models」資料夾如何傷害scalability? 看看Rails社群這篇出名的文章: 7 Patterns to Refactor Fat ActiveRecord Models 幹得好!它點出「胖胖entity」的問題,並給出7個patterns去協助你設計軟體架構。 抽出行為邏輯變成Service Objects、抽出表單驗證邏輯變成Form Objects、抽出資料庫 查詢邏輯變成Query Objects、抽出呈現邏輯變成View Objects…聽起來真棒,也確實很 有幫助,不是嗎? 問題來了:抽出來的這些類別,到底要放哪裡? 我們看看文章下面comments提到的範例:GItLab 它的檔案結構如下: /lib /gitlab /tasks /... /app /assets /controllers /finders /helpers /mailers /models /services /uploaders /views /workers 新的問題來了:那個「models」資料夾到底代表什麼?它是我們包羅萬象的偉大Model layer嗎?那finders、services、uploaders為什麼跟models在同一層,而不是在models 裡面?lib/底下的gitlab/又是怎麼回事?難道GitLab的商業邏輯也出現在lib? 這就是我想說的:「models」資料夾的存在從一開始就污染了架構設計。 它引誘人們把entity全丟進去。結果除了entity以外的東西,像是前面的Service Objects、 Form Objects、Query Objects、View Objects,還有後面的finders、 services、uploaders、gitlab全都不知道放哪了。只好隨便亂放。 GItLab原始碼中的models有代表MVC的M嗎?怎麼不改名叫entities? 就算改了又如何?Model layer到底在哪裡?四分五裂、結構鬆散。 一團混亂的設計、難以理解的命名、與MVC的M不相容的檔案結構。 所以我說,「models」資料夾傷害scalability! 那該怎麼辦? 要解決這個問題,首先得要了解MVC是三個極度不對稱的存在。 V: 負責呈現UI C: 負責接受request、請M處理、回傳response M: 負責全部的business logic M幾乎是你的整個application。 你可以在框架底下,找地方建一個空資料夾,用公司名稱或是專案名稱替它命名, 然後開始煩惱軟體架構這件事。 好好煩惱entity要放在哪裡、Service Objects、 Form Objects、Query Objects、View Objects、finders、services、uploaders這些要放哪裡,彼此又要怎麼分門別類。 MVC不是萬靈丹,只是軟體架構的入門磚。 架構設計本來就是這麼難,OOP本來就是這麼難。 恭喜你,至少你跨出第一步了: 你不再把一堆胖胖entity丟進「models」資料夾,然後覺得設計完軟體架構了。 Q&A Q1: 「models」資料夾毫無優點嗎? 「models」資料夾還是有少數優點。 它是一種quick and dirty作法,鼓勵你眼中只看見entity,然後把所有business logic 全塞進裡面。 換句話說,它在小型的專案可以幫你節省時間。但它的優點也僅此而已。 Q2: 你的結論好空泛,什麼建一個公司名稱空資料夾啊。拜託給點方向? 沒問題,我給你兩個架構設計的參考方向。 第一個來自這篇文章: Rails is Not Your Application 引用作者的話,核心精神如下: Rails不是你的application。它可以是你的views還有資料來源,但不是你的application 。把你的application放在Gem裡面或是lib/資料夾底下。 我不覺得這樣有很優雅,但至少點出一個可能方向,並且至少不再有models資料夾。 我第二個要給你的,是Laravel官方論壇的原始碼。 這個Laravel.io專案簡稱為Lio,結構如下: /app /controllers /views /Lio /Core /... /Accounts /User.php /UserPresenter.php /UserRepository.php /UserCreator.php /.... /Articles /Article.php /ArticlePresenter.php /ArticleRepository.php /ArticleCreator.php /.... /Comments /... 光看檔案結構就很優雅。也正是我前面所說的:建一個空資料夾,用公司名稱或是專案名 稱替它命名,然後開始煩惱架構設計這件事。 想想看Laravel官方論壇的原始碼為什麼長這樣吧。 Q3: 講得好像多有道理!我覺得你只是在鬼扯!框架的製作團隊都是業界大神,既然他們 決定要有「models」資料夾,必定有它的正當性! 不,你錯了。那些業界大神只是背負了行銷框架的壓力。 他們為了滿足用戶的錯誤期待而委屈地放了個「models」資料夾在那。 但還是有高尚的人存在。PHP最被推崇的框架Laravel就沒有「models」資料夾。 向Laravel的Taylor Otwell致敬吧! 他不願成為殘害新手的幫凶,硬是把「models」拿掉了, 強迫你去思考:「軟體架構到底該長怎樣」。 你要自己在Laravel裡面做一個「models」資料夾, 然後把那些entity class全丟進去嗎? 那你是自願把entity當成整個Model,真遺憾, 但別說是Laravel鼓勵你這麼做。Laravel盡力了。 Q4: 少自以為了解Taylor Otwell了!你憑什麼代替他發言! Reddit上有一則 Why would anyone choose Laravel over Symfony or Silex? Taylor Otwell本人親自做出回答。下文擷取自第四段: 我個人在開發Laravel 4的早期階段就想把「models」資料夾整個移除了。因為我不覺得 它有用,也不覺得它能協助你設計軟體架構。而且它還會引誘人們掉入「model == database」的陷阱裡。所以,我希望你不要覺得我對架構設計很無知。我花了點時間才想 清楚我到底想在PHP世界打造什麼。 Laravel實作Active Record Pattern,資料表映對到entity class。他指的「model == database」陷阱就是我說的「model == entity」錯覺。我並沒有代替他發言。 Q5: 我還是覺得,你沒有資格批評那麼多框架。「models」資料夾就是有某種正當性。 除了Taylor Otwell,我看也沒有其他權威支持你的說法! 前面提到的出色Stack Overflow問答: How should a model be structured in MVC? 作者是teresko Stack Overflow上關於MVC的幾個最高分討論,全都是由teresko解答。 下文擷取自那篇出色問答的段落「What a model is NOT」: model不是一個class,也不是任何一個單一物件。這是一個超級常見錯誤,因為大部分的 框架都在助長這種誤解。 他選擇這樣帶過。我選擇正面指控。 然後我建議你討論事情的時候,不要太在乎權威還是前輩怎麼講。 不如專注於討論事情本身。 Q6: 好啊!那來啊!照你的說法,「models」資料夾底下多放個「entities」資料夾不就 搞定一切問題了?你果然是不切實際的理想主義者!最好是有框架幹這麼囉唆的事情! 有!它就是Cake(PHP)框架! 看看Cake在Model底下放了什麼: /View /Controller /Model /Behavior /Entity /Table 看到了嗎? Cake怕你把entity當成整個model,直接擺好幾個資料夾, 逼你去思考entity跟model是什麼。 替這些用心良苦的框架歡呼吧! Q7: 專注於討論事情本身是不是!那Cake的「models」資料夾就沒問題啊!你還說任何框 架都不能有! 你看錯了,Cake沒有「models」資料夾,也沒有「Models」資料夾。它只有「Model」資 料夾。 資料夾、package、資料庫table命名,都有一個關於單數/複數的原則可以參考:異質性 與同質性。 你的「models」資料夾底下不再是同屬entity的class了,而是分為behavior、entity、 以及其他你設計的分類,也就是異質,所以應該用單數命名。 參考這個連結: Should package names be singular or plural? 簡單地說,既然model代表的是layer而非多個entity,資料夾命名上就應該用單數而非複 數。 好吧,我這樣說有點太嚴苛了。 如果你知道自己在幹嘛的話,就繼續用你的「models」資料夾吧,我勉強可以接受。 Q8: 等等,不對勁…你整篇文章流露一股氣息…我覺得你不但反對「models」,你幾乎在 否定MVC的價值?你怎麼可以覺得偉大的MVC沒有價值? 我前面提過,Taylor Otwell在Laravel 4移除了「models」資料夾,逼迫大家去思考「軟 體架構」到底應該是什麼。 我告訴你第二件事。 你去逛Laravel官網,翻遍官網你都找不到「MVC」三個字。 MVC名氣多麼響亮!哪個framework不想打著MVC當作賣點?但Laravel拒絕這麼做。 我再告訴你第三件事。 2015年最新出爐的Laravel 5,它的views在resources/底下,controllers在app/Http/底 下。一樣沒有models。 所以你神聖的MVC在Laravel 5底下長這樣: /resources /views /lang /assets /app /Commands /Console /Events /Exceptions /Handlers /Http /Providers /Services /Https /Controllers /Middleware /Requests 你推崇的V跟C不再佔據檔案結構的核心位置了。你最愛的MVC現在看起來是如此渺小, 小到沒有討論價值,小到毫無意義可言。 「MVC是三個極度不對稱的存在」,這是個太過客氣的說法。 MVC這個觀念已經無法協助我們討論和思考了。放下它,往前走吧。 我來自Laravel社群。我們不聲稱自己擁戴MVC。 來把時間花在真正值得討論的概念上吧:你正在用的框架,架構合理嗎?框架有沒有擋住 你的路?你在框架之下設計出的專案架構漂亮嗎?大中小型專案通用的架構存在嗎?如何 分辨使用時機?怎麼做會最彈性?該怎麼描述某個框架才不會對新手揠苗助長? 放下你凡事都要套進MVC的執著,請直接思考「軟體架構」的本質。 啊,我看到MVC粉絲對Laravel 5的分析了: 「 MVC依然發揮重要的討論價值!我看到Controllers資料夾了!我看到views資料夾了! 剩下的十幾個資料夾全部統稱為Model!果然是豐富又厚重的layer! 我們來討論Model是什麼吧!MVC萬歲! 」 朋友,祝福你能得出有意義的結論。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.37.93.112 ※ 文章網址: https://www.ptt.cc/bbs/Soft_Job/M.1427724167.A.916.html
Ghosso: 最近在看ios的框架,就改使用mvvm中 03/30 22:07
readonly: 你適合 django 03/30 22:08
GoalBased: 欸..沒看過胖的entity,甚至entity通常都是程式產的 03/30 22:10
GoalBased: 你對model資料夾的解釋,我換一個角度來講,根本就 03/30 22:11
GoalBased: 不該有view資料夾,那樣會讓新手以為跟畫面有關的東西 03/30 22:11
GoalBased: 都放在view資料夾裡面,結果你就會看到一個html 03/30 22:11
GoalBased: 一大堆css img js,其實不是甚麼該不該的問題 03/30 22:12
GoalBased: 那只是一個架構的概念,需要拆的人自然會去拆 03/30 22:12
CaptainH: 竟然有人寫blog的時候會想像別人在請教自己 ... 03/30 22:29
milonga332: 推 03/30 22:31
CaptainH: 立論薄弱的句讀之學 03/30 22:34
pttworld: 農曆七月又還沒到,鬼扯淡沒自己的重點。 03/30 22:43
motestg: MVC架構,有多種解釋,不過這種Model還是第一次見過,超詭 03/30 22:55
motestg: 異 03/30 22:55
LaPass: 這篇文,部分同意,部分覺得怪怪的。 03/30 23:07
LaPass: 有些框架的確把M直接當資料庫,但是做過erp就知道中間還要 03/30 23:10
LaPass: 做中間曾,要不然最後會血肉模糊。 03/30 23:11
LaPass: 但是,那跟不需要MODEL是兩回事,那也是MODEL 03/30 23:12
LaPass: 概念vs實作是兩回事,但這文把實作上的問題當作是概念問題 03/30 23:13
LaPass: ,總讓人覺得怪怪的。 03/30 23:14
jack0204: 分層寫在modules不是嗎? 為什麼要寫在models裡面? 03/30 23:27
mapleone: 把Models內的東西拆出去變成另一個專案就好了啊 03/30 23:52
leicheong: 我Model都放Entity的extension method, 用來告知如何 03/31 00:00
seamanku: LaPass 說得沒錯,原po將概念和實作混為一談了 03/31 00:01
leicheong: 解讀那entity的. 你可以想一下System.Drawings.Color 03/31 00:01
leicheong: 結構, 如何幫助我們方便地解讀32-bit ARGB value. 03/31 00:02
leicheong: GoalBased說得不錯. 那本來就是錯誤學習MVC架構的人 03/31 00:06
leicheong: 常犯的錯誤. 03/31 00:06
ROCKandROLL: 我是把 model 當作 database 的 interface 03/31 00:34
a926: 我從頭到尾都只認為MVC是架構的概念..就跟3-Tier一樣阿XD 03/31 00:47
appleway: 呃... 沒有被說服,並不同意這篇的看法。 03/31 00:50
GALINE: 我在想把 model 改名成 logic 是否能解決原 po 的抱怨... 03/31 02:35
GALINE: 我字也是 model 不該跟資料混為一談這一派的 03/31 02:37
GALINE: 自己 03/31 02:37
GALINE: 不過這算是從根本否定 active record pattern[汗] 03/31 02:38
GALINE: 這也是在不同的領域會有不同的需要,商業邏輯不會太複雜的 03/31 02:39
GALINE: 時候,寫在一起的 code 其實會優雅不少 03/31 02:39
GALINE: 但是商業邏輯開始膨脹的時候,很快就會變成麵團了 03/31 02:40
GALINE: 感覺這跟房間該怎麼整理一樣,小孩跟媽媽總是意見不同 03/31 02:41
talenttb: 這應該屬於團隊的共識,自己定義清楚就好了吧 03/31 08:31
csfgsj: 有反省、懷疑就給推,比一堆盲從的人好多了 03/31 08:54
liddle: 原po先去學系統論吧。 03/31 10:01
StupidGaGa: 我覺得原PO該問問小朱大,原PO觀念已經偏掉了 03/31 10:03
StupidGaGa: 我覺得原PO發表文章可以先從分享程式碼開始 03/31 10:05
StupidGaGa: 而且很多事情不要一知半解就拿出來發表,半杯水響叮噹 03/31 10:05
qrtt1: kaif 上也有些討論 xd 03/31 10:16
hSATAC: models 資料夾最好是會傷害 scalability... 03/31 11:04
hSATAC: 然後 7 patterns 那篇叫 "refactor" 好嗎? refactor! 03/31 11:04
strlen: 老實說原PO這篇文不一定對 但總也算是個拋磚引玉吧? 不同 03/31 11:56
strlen: 意可以寫一篇反駁他的文讓大家長長知識阿 一擊脫離對架構 03/31 11:57
strlen: 思考也不會有什麼幫助吧? 小弟我老實說還是不太了有M跟沒 03/31 11:58
qrtt1: http://bit.ly/1BZAqdO 推薦 ihower 的分享 03/31 11:59
qrtt1: 題目:從 Classes 到 Objects:那些OOP教我的事 03/31 11:59
strlen: M真的有差這麼大? 一直以來都以為MVC就只是個方便團隊作 03/31 11:59
strlen: 業的東西 約定成俗就好 如果團隊不想用M 那也OK阿 03/31 11:59
pennymarkfox: 我真的看過胖胖entity...所以我還滿認同這部份的 03/31 12:11
gname: mvc就是個"概念",不用這麼認真啦...諸多概念中的一個~就醬 03/31 12:38
typiacalcat: 衝blog點擊也不用這樣... 是要強化業務技能樹嗎? :/ 03/31 12:46
remmurds: 只有我覺得爭這個很沒意義嗎? 03/31 14:43
bibo9901: "胖胖"跟叫什麼名字無關啊, 切成 services, helpers 就 03/31 15:55
bibo9901: 不會胖胖的嗎? 還是會啊, so? 03/31 15:55
bibo9901: 難道叫 models 你就不會抽像化也不會重構了嗎? 03/31 15:55
leicheong: 吵這個就跟吵RMDB要不要跑到3NF一樣, 不過意外地看到 03/31 19:37
leicheong: 這居然有「傷害scalability」作為論點就... 03/31 19:38
vn509942: 我是另外再細切成Repository、Service 方便分工 04/01 21:13
vn509942: 中間再放個ViewModel 04/01 21:14
takasaki: ....我都爽叫models,我還會開repository service等等 04/02 10:57
manaup: 我煮味噌湯都只放木瓜和排骨 喝起來有九成像木瓜排骨湯 04/02 13:02
psliurt: MVC是概念,有Model資料夾只是某些語言的約定俗成 04/05 22:28