本篇介紹如何在 Laravel 中導入 Repository Pattern 與 Service Layer,將商業邏輯從 Controller 分離,提升可測試性與可維護性。
為什麼需要分層架構?
大多數 Laravel 專案初期都是 Fat Controller,所有邏輯堆在一起:
// 典型的 Fat Controller(難以維護、無法測試)
public function store(Request $request)
{
$user = User::where('email', $request->email)->first();
if ($user) {
return response()->json(['message' => 'Email 已存在'], 422);
}
$user = User::create([...]);
$user->roles()->attach(Role::where('name', 'member')->first());
Mail::to($user)->send(new WelcomeMail($user));
Cache::forget('users:list');
Log::info('新用戶註冊', ['user_id' => $user->id]);
return response()->json(['message' => '註冊成功']);
}
問題:Controller 同時負責驗證、資料庫操作、商業邏輯、快取、通知,一旦需求改變就要動到 Controller,無法單元測試。
三層架構設計
| 層次 | 職責 | 不應該做的事 |
|---|---|---|
| Controller | 接收 Request、呼叫 Service、回傳 Response | 直接操作 DB、寫商業邏輯 |
| Service | 商業邏輯、流程控制、跨 Repository 協調 | 直接操作 DB(透過 Repository) |
| Repository | 所有資料庫查詢與寫入 | 寫商業邏輯 |
實作流程(以 User 模組為例)
Step 1:建立 Repository Interface
# app/Repositories/Contracts/UserRepositoryInterface.php
Step 2:建立 Eloquent 實作
# app/Repositories/EloquentUserRepository.php
Step 3:綁定 Interface → 實作
# app/Providers/AppServiceProvider.php
use App\Repositories\Contracts\UserRepositoryInterface;
use App\Repositories\EloquentUserRepository;
public function register(): void
{
$this->app->bind(UserRepositoryInterface::class, EloquentUserRepository::class);
}
Step 4:建立 Service Layer
# app/Services/UserService.php
Step 5:Controller 只注入 Service
# app/Http/Controllers/UserController.php
單元測試的好處
導入分層後,Service 可以 Mock Repository,完全不需要碰真實資料庫:
class UserServiceTest extends TestCase
{
public function test_register_throws_exception_when_email_exists()
{
// Mock Repository 回傳已存在的 User
$mockRepo = Mockery::mock(UserRepositoryInterface::class);
$mockRepo->shouldReceive('findByEmail')
->once()
->andReturn(new User());
$service = new UserService($mockRepo);
$this->expectException(\Exception::class);
$service->register(['email' => '[email protected]']);
}
}
目錄結構
app/
├── Http/
│ └── Controllers/
│ └── UserController.php # 只管 Request / Response
├── Services/
│ └── UserService.php # 商業邏輯
├── Repositories/
│ ├── Contracts/
│ │ └── UserRepositoryInterface.php
│ └── EloquentUserRepository.php # 資料庫操作
└── Providers/
└── AppServiceProvider.php # Interface 綁定
什麼時候不需要用?
分層架構是有成本的,以下情境可以斟酌:
- CRUD 簡單的小型專案:直接用 Controller + Model 就夠了
- 原型開發、快速驗證:先求跑通,之後有需要再重構
- 團隊規模小、沒有測試需求:分層反而增加溝通成本
分層的核心價值在於:商業邏輯複雜、需要單元測試、多人協作的中大型專案。
參考文獻:
No comments yet.