你的 Laravel 專案是技術債炸彈還是傳世藝術品?Laravel 10 專案架構最佳實務指南
嗨,我是浪花科技的 Eric。身為一個整天跟程式碼打交道的工程師,我看過太多專案的「黑歷史」了。有些專案一打開,那錯綜複雜的依賴關係、肥大的 Controller、上帝般的 Model,簡直比義大利麵還糾結,我們通常稱之為「Spaghetti Code」。每次要加個小功能,就像在拆炸彈,深怕一不小心就引爆隱藏的 bug,搞得整個團隊人仰馬翻。
這就是為什麼我今天非得來囉嗦一下「Laravel 10 專案架構最佳實務」。一個好的專案架構,就像一棟建築的藍圖。地基打得穩,未來不管是要擴建、裝修,還是應對突發狀況(例如流量暴增),都能從容不迫。反之,一個混亂的架構,就是不斷累積的「技術債」,初期或許開發飛快,但很快地你就會發現,每走一步都舉步維艱,最後整個專案變成一座難以維護的危樓。
今天,我不想只談理論,而是要帶你一步步實作,將 Laravel 這個優雅的框架,打造成一個既穩固又具備高度擴展性的開發堡壘。準備好了嗎?讓我們開始吧!
超越 MVC:打造可擴展的 Laravel 服務層 (Service Layer)
Laravel 骨子裡是個 MVC (Model-View-Controller) 框架,這本身沒什麼問題。但問題在於,很多開發者(包括年輕時的我)會把所有的商業邏輯一股腦地塞進 Controller 或 Model 裡,這就是萬惡的開端——「肥 Controller」和「肥 Model」問題。
當 Controller 處理了太多不屬於它職責的商業邏輯(例如:計算訂單總額、呼叫第三方 API、寄送通知信),它就變得臃腫且難以測試。當 Model 包含了太多商業規則,它就不再只是一個單純的資料對應層。為了解決這個問題,我們引入「服務層」(Service Layer)。
什麼是服務層 (Service Layer)?
簡單來說,服務層是一個專門用來放置和組織商業邏輯的地方。它像是一個協調者,介於 Controller 和 Model/Repository 之間。Controller 的職責很單純:接收 HTTP Request,呼叫對應的 Service 處理,然後回傳 HTTP Response。所有複雜的商業流程,都應該封裝在 Service 裡面。
如何在 Laravel 中實作服務層?
我們通常會在 app 目錄下建立一個新的 Services 資料夾。假設我們要處理使用者註冊的邏輯,可能會包含建立使用者、發送歡迎信、記錄 Log 等操作。
首先,建立 app/Services/UserService.php:
<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;
class UserService
{
public function register(array $data): User
{
// 1. 建立使用者資料
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
// 2. 寄送歡迎信
Mail::to($user->email)->send(new WelcomeMail($user));
// 3. 可以在這裡加上其他邏輯,例如記錄 Log 或觸發其他事件
return $user;
}
}
接著,你的 UserController 會變得非常清爽:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\RegisterRequest; // 使用 Form Request 進行驗證
use App\Services\UserService;
class UserController extends Controller
{
protected $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function register(RegisterRequest $request)
{
$user = $this->userService->register($request->validated());
// 返回成功的回應,例如 token 或重導向
return response()->json(['message' => 'User registered successfully!', 'user' => $user], 201);
}
}
看到了嗎?Controller 只負責「溝通」,它不關心註冊的具體步驟。所有的商業邏輯都被漂亮地封裝在 UserService 中,這讓程式碼更容易閱讀、維護和測試。
Repository 模式:解耦你的資料庫邏輯
有了服務層後,下一個優化的目標就是資料存取邏輯。很多時候,我們的 Service 裡面會混雜著大量的 Eloquent 查詢。這會讓 Service 和 Eloquent ORM 緊緊地綁在一起,如果未來想換資料庫、改用不同的 ORM,甚至只是想寫個單元測試,都會變得很痛苦。
這時候,「Repository 模式」就派上用場了。它的核心思想是建立一個抽象層,將資料存取邏輯(例如資料庫查詢)從商業邏輯中分離出來。
Repository 模式的優點
- 集中管理資料查詢: 所有與特定 Model 相關的查詢都放在同一個 Repository 中,方便管理。
- 提升可測試性: 在測試 Service 時,我們可以輕易地用一個假的 (Mock) Repository 來取代真實的資料庫操作。
- 資料來源抽象化: 你的商業邏輯不關心資料是從 MySQL、Redis 還是某個外部 API 來的,它只跟 Repository 介面溝通。
實戰:建立你的第一個 Repository
我們會建立一個介面 (Interface) 和一個實作 (Implementation)。介面定義了「要做什麼」,而實作則定義了「怎麼做」。
1. 在 app 下建立 Contracts/Repositories 和 Repositories/Eloquent 資料夾。
2. 建立介面 app/Contracts/Repositories/UserRepositoryInterface.php:
<?php
namespace App\Contracts\Repositories;
use App\Models\User;
interface UserRepositoryInterface
{
public function create(array $data): User;
public function findByEmail(string $email): ?User;
}
3. 建立 Eloquent 實作 app/Repositories/Eloquent/EloquentUserRepository.php:
<?php
namespace App\Repositories\Eloquent;
use App\Contracts\Repositories\UserRepositoryInterface;
use App\Models\User;
class EloquentUserRepository implements UserRepositoryInterface
{
public function create(array $data): User
{
return User::create($data);
}
public function findByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
}
4. 最後,我們需要告訴 Laravel,當有人請求 UserRepositoryInterface 時,要給他 EloquentUserRepository 的實例。這可以在 app/Providers/RepositoryServiceProvider.php 中設定(如果沒有這個檔案,可以手動建立並在 config/app.php 中註冊)。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Contracts\Repositories\UserRepositoryInterface;
use App\Repositories\Eloquent\EloquentUserRepository;
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
UserRepositoryInterface::class,
EloquentUserRepository::class
);
}
}
現在,我們可以重構 UserService,讓它依賴於介面而不是具體的 Eloquent Model:
<?php
namespace App\Services;
use App\Contracts\Repositories\UserRepositoryInterface;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;
class UserService
{
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function register(array $data): User
{
$user = $this->userRepository->create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
Mail::to($user->email)->send(new WelcomeMail($user));
return $user;
}
}
你看,UserService 現在完全不知道資料庫是怎麼運作的。它只知道有一個符合 UserRepositoryInterface 契約的物件可以幫它處理資料,這就是所謂的「依賴反轉原則」,也是乾淨架構的核心精神之一。
Action 類別:處理單一、獨立的操作
有時候,某些操作非常單純,它就是一個獨立的、一次性的任務,例如「處理一筆付款」、「發布一篇文章」或「產生一份報告」。如果為這些單一操作硬是建立一個 Service,似乎有點小題大作。這時候,「Action」類別就登場了。
Action 是一個小巧、專注的類別,通常只有一個公開的方法(例如 execute 或 __invoke),專門用來執行一項特定的任務。
Action vs. Service:我該用哪個?
這是我常被問到的問題。你可以這樣想:
- Service: 處理一個完整的「業務場景」,可能包含多個步驟和邏輯判斷。例如,「使用者註冊服務」可能包含建立用戶、發信、寫 Log 等多個動作。
- Action: 處理一個「單一動詞」的操作。它只做一件事,並把它做好。例如,「發布文章」這個動作。
在實務上,它們並不互斥,一個 Service 內部也可能呼叫多個 Actions 來完成複雜的任務。
建立一個 Action Class
我們可以在 app 目錄下建立一個 Actions 資料夾。例如,我們要建立一個發布文章的 Action。
app/Actions/Posts/PublishPostAction.php:
<?php
namespace App\Actions\Posts;
use App\Models\Post;
class PublishPostAction
{
public function execute(Post $post): Post
{
if ($post->is_published) {
// 可以拋出一個例外
throw new \Exception('This post is already published.');
}
$post->published_at = now();
$post->is_published = true;
$post->save();
// 可以在這裡觸發事件,例如通知訂閱者
return $post;
}
}
在 Controller 中使用它,會讓程式碼的意圖變得極度清晰:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use App\Actions\Posts\PublishPostAction;
class PostController extends Controller
{
public function publish(Post $post, PublishPostAction $publishPostAction)
{
$publishPostAction->execute($post);
return back()->with('success', 'Post published successfully!');
}
}
Controller 的職責就是接收請求,然後委派給對應的 Action 去執行。每一行程式碼都在講述一個清楚的故事,這對於長期維護專案來說,價值連城。
專案結構的最終藍圖:一個可供參考的目錄結構
結合了服務層、Repository 模式和 Action 之後,一個清晰、可擴展的 Laravel 專案結構藍圖就浮現了。這是一個我個人在許多中大型專案中採用的結構,你可以作為參考:
app/
├── Actions/ # 放置單一職責的 Action 類別
├── Console/
├── Contracts/ # 放置介面 (Interfaces)
│ └── Repositories/
├── Exceptions/
├── Http/
│ ├── Controllers/ # 保持 Controller 輕薄
│ ├── Middleware/
│ └── Requests/ # 處理所有請求驗證
├── Jobs/ # 處理隊列任務
├── Listeners/ # 事件監聽器
├── Mail/
├── Models/ # Eloquent Models
├── Providers/
│ └── RepositoryServiceProvider.php
├── Repositories/ # Repository 實作
│ └── Eloquent/
└── Services/ # 處理複雜的商業邏輯
請記住,這不是唯一的標準答案,但它提供了一個非常好的起點。其中,特別要強調 Http/Requests 的使用,務必將所有的表單驗證邏輯都放在 Form Request Class 中,這能讓你的 Controller 保持乾淨,專注於協調工作。
架構不是死的,而是演化的藝術
說了這麼多,最後還是要囉嗦一句:沒有所謂「最好」的架構,只有「最適合」當下專案規模與複雜度的架構。對於一個簡單的個人部落格,或許直接在 Controller 寫邏輯也無傷大雅。但如果你正在打造一個企業級的應用、一個預期會長期營運和迭代的產品,那麼從一開始就投入時間去思考和設計一個穩固的架構,絕對是回報率最高的投資。
好的架構能讓你的團隊協作更順暢,讓新成員能更快上手,讓未來的你在回頭看程式碼時,不會想掐死當初的自己。它讓你的專案從一團混亂的義大利麵,變成一塊塊可以獨立測試、替換和組合的樂高積木。這不僅是技術上的追求,更是一種工程師的浪漫與藝術。
相關閱讀
- 告別雜亂無章!資深工程師帶你走進 Laravel Admin 後台架構設計的藝術
- 肥 Controller 瘦不下來?Laravel 後台架構終極對決:Repository vs. Action 模式,資深工程師帶你選對屠龍刀!
- Eloquent 不只是 CRUD!資深工程師揭秘 Laravel ORM 進階戰術與效能黑魔法
需要專業的 Laravel 架構規劃與開發協助嗎?
如果你正在規劃新的 Laravel 專案,或是現有的專案遇到了維護瓶頸,卻不知道從何下手進行重構。浪花科技的團隊擁有豐富的企業級 Laravel 專案開發與架構設計經驗,我們樂於協助你打造穩固、高效且易於維護的應用程式。別讓混亂的架構拖慢你的業務發展,歡迎點擊這裡,與我們的技術顧問聊聊,讓我們一起打造出色的產品!
常見問題 (FAQ)
Q1: 我需要在所有 Laravel 專案都使用服務層和 Repository 模式嗎?
A: 不一定。對於規模非常小、邏輯單純的 CRUD (增刪查改) 應用,直接在 Controller 中使用 Eloquent 可能是最快的方法,引入這些模式反而會增加不必要的複雜度(Over-engineering)。但是,只要你的專案預期會成長、功能會增加,或是有多位開發者協作,那麼及早導入服務層與 Repository 模式,絕對是明智之舉,能有效避免未來龐大的技術債。
Q2: Action、Service、Repository 到底有什麼不同?我該如何選擇?
A: 你可以這樣簡單區分它們的職責:
- Repository (儲存庫): 專門負責「資料存取」。它的任務是跟資料庫或其他資料來源溝通,把資料拿出來或存進去。
- Service (服務): 專門負責「商業邏輯」。它負責協調一個完整的業務流程,可能會呼叫多個 Repository 或其他 Service 來完成任務。
- Action (動作): 專門負責「單一操作」。它是一個獨立的、專注的任務,像是「寄送一封信」或「更新使用者頭像」。
在選擇上,可以依據操作的複雜度。一個簡單的資料查詢,直接透過 Repository 即可;一個獨立的動作,用 Action;一個複雜的業務場景,則用 Service 來統籌。
Q3: Laravel 10 的預設專案架構跟舊版有什麼重大差異嗎?我介紹的這些模式是新功能嗎?
A: Laravel 10 的預設專案目錄結構與近幾個版本(如 Laravel 8、9)相比,並沒有根本性的巨大變化。本文介紹的服務層、Repository 模式和 Action 類別,並不是 Laravel 框架內建的「特定功能」,而是軟體工程領域中廣泛被使用的「設計模式」(Design Patterns)。這些模式之所以在 Laravel 社群中如此受歡迎,是因為 Laravel 的服務容器 (Service Container) 和依賴注入 (Dependency Injection) 機制,讓實踐這些乾淨、解耦的架構模式變得非常方便和優雅。






