Laravel 的中大型專案架構
只有MVC 是不夠的,我們需要更完整的專案架構
初學者學習Laravel時分兩種,一種是乖乖的將程式填入MVC架構內,導致controller與model異常的肥大,日後一樣很難維護;一種是常常不知道程式該寫在哪一個class內而猶豫不決,畢竟傳統PHP都是一個頁面一個檔案。本文整理出最適合Laravel的中大型專案架構,兼具容易維護、容易擴充與容易重複使用的特點,並且容易測試。
Version
Laravel 5.1.24
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模式。
箭頭表示物件依賴注入的方向。11關於依賴注入,詳細請參考深入探討依賴注入
我們可以發現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。
-
最後搭配單元測試,測試重構後的架構是否與原來的需求結果相同。