2020年6月13日

PHP | Laravel | 適合中小型專案的簡易分層結構

為什麼要分層?

適度的分層可以幫助我們整理、歸類程式,維持程式庫的整潔性,除了方便我們管理程式,日後團隊協作時,也可以以依照層級劃分工作,促進團隊開發效率,也能降低溝通成本。

如何分層?

Service層(商業邏輯層)

通常程式可以被粗略地分成兩種,商業邏輯跟非商業邏輯的部分。

商業邏輯其實就是這個系統被創造的原因,我們利用程式、串連各種系統資源,讓資料變更或流向我們想要的地方,來促進商業行為的發生。

但是這樣的商業邏輯程式碼,如果沒有經過整理,將會非常雜亂。

有些人會把所有商業邏輯都寫在Model中,有些人則會寫在Controller。

寫在Model中的缺點是,Model處理的事情過多,若以訂單Model為例,包含成立訂單、取消訂單、修改訂單及其相關的細節,都要在訂單Model中處理的話,Model會長成巨無霸。

如果都在Controller中處理,則程式碼則會零散在各個Controller,一定會有部分邏輯處理是相通的,卻無法重複使用。

因此可以把商業邏輯獨立出來變成Service層。

將商業邏輯都集中在Service層中,Controller中可以直接呼叫Service,來處理商業邏輯。

而Service則再透過Model來對DB做操作。

對Controller來說,產生訂單會變成類似這樣的用法:

$createOrderService->createOrder($orderData);

當今天有複數以上的Controller也要產生訂單,只要在Controller也使用同樣的Service即可。或是如同文章一開始所展示的圖片,自訂的Command,也可以使用Service所提供的function,來完成指令。

Repository層

Repository層主要的用途是集中管理取得與更新資料的部分。

雖然Laravel Model可以讓你很輕鬆地組出SQL去取得或更新資料,但若隨著系統成長,這樣的程式零散在各地,即便是零散在前面所說的Service中,都會造成管理上跟維護上的困擾。

如果我們將Respository層分出來,實際上的用法大概會像這樣:

$order = $orderRepository->get($orderId);
...
$orderRepository->add($order);

將原本在Controller或是Service中直接透過model去跟DB做互動的程式,通通放置到Repository之中。

而除了整理程式以外,Repository還有兩個用途:

  1. SQL, Database這些東西,其實算是實作上的細節,如果我們將這些東西抽離出主要商業邏輯程式,可以降低主要程式跟資料庫(也就是外部系統)的相依性。
  2. 比較方便進行單元測試,可以更容易注入mock後的repository。

分層後概況

以Laravel 5.8為例,分層後的目錄結構大概會長這樣。

app
│   
└───Models
│   │   OrderModel   
│   
└──-Repositories
│   │   OrderRepository
│   
└───Services
│   │   CreateOrderService   
│   │   CancelOrderService
│   
└───Http/Controllers
│   │   OrderController   

總結

這個簡單的分層方式,對於一般中小型專案應該足夠使用了。但不會有一個架構可以符合所有的需求,還是要視情況調整。

參考

What are the benefits of using Repositories?

【Larave學習書籍】 Laravel 啟動與運行(第二版)