Laravel的中大型項目構架

初學者學習Laravel時分兩種,一種是乖乖的將程序填入MVC構架內,導致controller與model異常的肥大,日後一樣很難維護;一種是常常不知道程序該寫在哪一個class內而猶豫不決,畢竟傳統PHP都是一個頁面一個檔案。本文整理出最適合Laravel的中大型項目構架,兼具容易維護、容易擴充與容易重複使用的特點,並且容易測試。

Controller過於肥大
受RoR的影響,初學者常認爲MVC構架就是model,view,controller:

Model就是數據庫。
Controller負責與HTTP溝通,調用model與view。
View就是HTML。
假如依照這個定義,以下這些需求該寫在哪裏呢?

發送Email,使用外部API。
使用PHP寫的邏輯。
依需求將顯示格式作轉換。
依需求是否顯示某些數據。
依需求顯示不同數據。
其中1,2屬於商業邏輯,而3,4,5屬於顯示邏輯,若依照一般人對MVC的定義,model是數據庫,而view又是HTML,以上這些需求都不能寫在model與view,只能勉強寫在controller。
因此初學者開始將大量程序寫在controller,造成controller的肥大難以維護。

Model過於肥大
既然邏輯寫在controller不方便維護,那我將邏輯都寫在model就好了?

當你將邏輯從controller搬到model後,雖然controller變瘦了,但卻肥了model,model從原本代表數據庫,現在變成還要負擔商業邏輯與顯示邏輯,結果更慘。
Model代表數據庫嗎?把它想成是Eloquent class就好,數據庫邏輯應該寫在repository裏,這也是爲什麼Laravel 5已經沒有models目錄,Eloquent class僅僅是放在app根目錄下而已。

中大型項目構架
那我們該怎麼寫呢?別將我們的思維侷限在MVC內:

Model:僅當成Eloquent class。
Repository:輔助model,處理數據庫邏輯,然後注入到service。
Service:輔助controller,處理商業邏輯,然後注入到controller。
Controller:接收HTTP request,調用其他service。
Presenter:處理顯示邏輯,然後注入到view。
View:使用blade將數據binding到HTML。

其中藍色爲原本的MVC,而紫色爲本文要介紹的的重點:Repository模式,Service模式與Presenter模式。
箭頭表示物件依賴注入的方向。
我們可以發現MVC構架還在,由於SOLID的單一職責原則與依賴反轉原則:

我們將數據庫邏輯從model分離出來,由repository輔助model,將model依賴注入進repository。
我們將商業邏輯從controller分離出來,由service輔助controller,將service依賴注入進controller。

我們將顯示邏輯從view分離出來,由presenter輔助view,將presenter依賴注入進view。
建立目錄
在 app 目錄建立 Repositories,Services 與 Presenters 目錄。

別害怕在Laravel預設目錄以外建立的其他目錄,根據SOLID的單一職責原則,class功能越多,責任也越多,因此越違反單一職責原則,所以你應該將你的程序分割成更小的部分,每個部分都有它專屬的功能,而不是一個class功能包山包海,也就是所謂的萬能類別,所以整個項目不應該只有MVC三個部分,放手根據你的需求建立適當的目錄,並將適當的class放到該目錄下,只要我們的class有namespace幫我們分類即可。
Repository

由於篇幅的關係,將repository獨立成專文討論,請參考如何使用Repository模式?
Service

由於篇幅的關係,將service獨立成專文討論,請參考如何使用Service模式?
Presenter

由於篇幅的關係,將presenter獨立成專文討論,請參考如何使用Presenter模式?
單元測試

由於現在model、view、controller的相依物件都已經拆開,也都使用依賴注入,因此每個部分都可以單獨的做單元測試,如要測試service,就將repository加以mock,也可以將其他service加以mock。
Presenter也可以單獨跑單元測試,將其他service加以mock,不一定要跑驗收測試才能測顯示邏輯。
Conclusion
本文談到的構架只是開始,你可以依照實際需求增加更多的目錄與class,當你發現你的MVC違反SOLID原則時,就大膽的將class從MVC拆開重構,然後依照以下手法:
建立新的class或interface。
將相依物件依賴注入到class。
在class內處理他的職責。
將class或interface注入到controller或view。
最後搭配單元測試,測試重構後的構架是否與原來的需求結果相同。

發佈了33 篇原創文章 · 獲贊 17 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章