代碼分層設(shè)計實踐與總結(jié)
簡介
見過很多PHP開發(fā)者的代碼,在代碼分層上面都不是很注重。一般都是控制器負責所有的業(yè)務(wù)邏輯,在控制器中調(diào)用模型做數(shù)據(jù)操作、驗證數(shù)據(jù)也在控制器中等等情況。這樣的做法怎么說呢?也沒錯,但是這樣寫代碼就顯示的很雜糅。
本文分享一些個人的代碼分層想法,存在不足的地方,希望大家多多提出一些寶貴建議。
文章底部有代碼示例連接,可以直接通過代碼查看或許更加方便。
相關(guān)技術(shù)
Laravel資源控制器、Laravel模型、PHP對象接口
實現(xiàn)思路
大致實現(xiàn)的思路如下:
// uml圖
@startuml
controller->service:調(diào)用
service->repository:調(diào)用
repository->model:調(diào)用
@enduml
controller層直接調(diào)用service層,controller主要負責傳遞請求參數(shù),返回接口數(shù)據(jù)。 service層負責處理數(shù)據(jù)邏輯,將controller接收到的參數(shù)格式化,然后將整理好的數(shù)據(jù)傳遞給repository層。 repository層直接調(diào)用model層的示例,進行數(shù)據(jù)操作。 model層主要責任是映射數(shù)據(jù)表,定義一個有關(guān)數(shù)據(jù)表的操作。例如表名、時間錯、獲取器和修改器等等。
代碼演示
首先定義了如下的目錄結(jié)構(gòu),具體的其他結(jié)構(gòu)可以根據(jù)自己的需要來定義,例如驗證層、接口響應(yīng)層、資源層等等。
為了保證在controller、service、repository層中的相關(guān)方法名稱以及返回參數(shù)格式都保持一致,在每一個層,都定義一個接口,接口中的方法都定義好參數(shù)格式以及返回值類型。例如controller層。首先我們定義一個controller層接口.
<?php
namespace App\Http\Controllers;
/**
* Api controller service
*
* Interface ApiServiceController
* @package App\Http\Controllers
*/
interface ApiServiceController
{
/**
* 具體每個方法的定義參考laravel文檔
* https://learnku.com/docs/laravel/5.8/controllers/3893#resource-controllers
*/
public function index();
public function create();
public function store();
public function show();
public function edit();
public function update();
public function destroy();
}
對應(yīng)的實現(xiàn)類controller,實現(xiàn)ApiServiceController接口。
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\ApiAuthBaseController;
use App\Http\Controllers\ApiServiceController;
use App\Services\UserInterface;
/**
* User's controller
*
* Class UserController
* @package App\Http\Controllers\User
*/
class UserController extends ApiAuthBaseController implements ApiServiceController
{
public function __construct(UserInterface $apiService)
{
$this->service = $apiService;
parent::__construct($apiService);
}
public function index()
{
$items = $this->service->serviceIndex((array)$this->requestParams);
return response()->json([
'code' => 10001,
'msg' => 'select success',
'data' => $items,
]);
}
public function create()
{
// TODO: Implement create() method.
}
public function store()
{
if ($this->service->serviceStore((array)$this->requestParams)) {
return response()->json([
'code' => 10001,
'msg' => 'create success',
'data' => [
'id' => 1,
],
]);
}
}
public function show()
{
// TODO: Implement show() method.
}
public function edit()
{
// TODO: Implement edit() method.
}
public function update()
{
if ($this->service->serviceUpdate((array)$this->requestParams)) {
return response()->json([
'code' => 10001,
'msg' => 'update success',
'data' => [
],
]);
}
}
public function destroy()
{
if ($this->service->serviceDestroy($this->requestParams)) {
return response()->json([
'code' => 10001,
'msg' => 'delete success',
'data' => [
],
]);
}
}
}
對應(yīng)的service層、repository層都根據(jù)類似的方式定義。具體的實現(xiàn)方法可以參考文章底部的代碼示例。
接口調(diào)用演示
根據(jù)上面的代碼演示邏輯,假設(shè)我們定義好了service層和repository層對應(yīng)的邏輯,這時候我們Api添加一個資源路由的定義就可以直接調(diào)用啦。在api.php路由文件定義如下格式:
<?php
use Illuminate\Support\Facades\Route;
Route::resource('user', 'User\UserController');
接下來,我們查看一下調(diào)用結(jié)果。1.增加數(shù)據(jù)。
2.刪除數(shù)據(jù)。
3.修改數(shù)據(jù)。
4.查詢數(shù)據(jù)。
總結(jié)
本文總結(jié)只是屬于個人的一些總結(jié),存在不足的地方,歡迎大家指正。這里總結(jié)一下設(shè)計這一的思路。
使用資源路由,我們直接定義一個路由規(guī)則,增刪改查等接口方式,我們就自動實現(xiàn)并且能夠規(guī)范團隊中的接口,同時也符合RESTful API的規(guī)范。
使用接口定義一些業(yè)務(wù)邏輯函數(shù),實現(xiàn)類直接實現(xiàn)接口中的方法,這樣可以避免團隊方法定義不一致、接口參數(shù)不一致、返回參數(shù)不一致等情況。如果接口中方法沒有定義,然而業(yè)務(wù)邏輯需要單獨一個方法,可以直接在實現(xiàn)類中定義獨有的方法即可。
model層主要實現(xiàn)表映射關(guān)系,這里直接把表當做模型。因此所有的邏輯不應(yīng)該在模型層中處理,頂多定義一個屬性等情況。repository層直接去調(diào)用model層,不需要處理數(shù)據(jù)格式等情況,根據(jù)service層傳遞的條件,將查詢的數(shù)據(jù)直接返回給service層。service層則是負責業(yè)務(wù)邏輯處理,比如格式化接口請求參數(shù)、組裝查詢條件、刪除條件等情況。controller則是負責將請求的參數(shù)傳遞給service層,然后將service層返回的數(shù)據(jù)返回給客戶端。這樣每一層負責的職能獨立,互補關(guān)聯(lián)。降低了代碼的耦合度。
使用資源路由,簡化接口。
示例代碼
代碼地址https://gitee.com/bruce_qiq/laravel-design
