Roy-Try-Catch
← Back to list

【設計模式】Repository Pattern + Service Layer 在 Laravel 的實踐

Roy • Updated 2026-02-27 11:03:43

本篇介紹如何在 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 就夠了
  • 原型開發、快速驗證:先求跑通,之後有需要再重構
  • 團隊規模小、沒有測試需求:分層反而增加溝通成本

分層的核心價值在於:商業邏輯複雜、需要單元測試、多人協作的中大型專案。

參考文獻:

https://laravel.com/docs/11.x/container

https://laravel.com/docs/11.x/providers

Comments

No comments yet.

請先登入