作者poopoo888888 (阿川)
看板Soft_Job
標題[心得] 框架不應該有「MODELS」資料夾
時間Mon Mar 30 22:02:44 2015
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: 題目:從 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