~/blog/laravel-10-project-architecture-best-practices-guide-2.md
Laravel 與後端開發 · 2026 / 01 / 23

Laravel 10 專案架構能不能傳世?分層設計與最佳實務指南

Eric — 浪花科技創辦人 / AI 架構師
Eric
浪花科技創辦人 · AI 架構師
Laravel 10 專案架構能不能傳世?分層設計與最佳實務指南
目錄 table-of-contents.md

能跑的程式碼跟能傳世的架構,中間隔著十年的技術債。Controller 塞 500 行商業邏輯、Model 互相依賴成打結的耳機線、View 裡直接寫 SQL——這些「陳年義大利麵」我在接手的專案裡看過太多次。這篇整理 Laravel 10 的分層設計與最佳實務,讓你的專案五年後還有人敢接手。

Laravel 是一個非常自由的框架,這也是它的雙面刃。Laravel 10 引入了許多強大的 PHP 8.1+ 特性,如果你還在用 Laravel 5 的思維寫 Laravel 10,那你正在錯過讓專案從「玩具」進化成「企業級應用」的機會。今天,我們不談太高深的微服務拆分,我們來談談在單體式架構(Monolithic)下,如何透過Laravel 10 專案架構最佳實務,把你的程式碼寫得像首詩,而不是一場災難。

1. 擁抱強型別 (Type Hints) 與回傳類型

Laravel 10 的應用程式骨架(Skeleton)已經全面擁抱 PHP 的型別系統。這不是為了裝酷,而是為了讓你的程式碼「剛性」更強,減少那些因為 null 或是錯誤資料型態導致的隱形 Bug。

在 Laravel 10 中,請強迫自己和團隊加上參數型別與回傳型別宣告 (Return Types)。這能讓 IDE 更好地輔助你,也能讓程式碼本身即文件。

不推薦的寫法 (Loose Typing):

public function store($request)
{
    // 這裡的 $request 是什麼? array? Request 物件?
    // 回傳的是什麼? JSON? View?
    // 這種寫法要看完整個 function 才知道在幹嘛
}

Laravel 10 推薦寫法 (Strict Typing):

use Illuminate\Http\RedirectResponse;
use App\Http\Requests\StoreUserRequest;

public function store(StoreUserRequest $request): RedirectResponse
{
    // 清楚知道輸入是有驗證過的 Request,輸出必定是轉址
    $this->userService->create($request->validated());

    return to_route('users.index');
}

2. 拒絕肥大的 Controller:善用 Service Layer 與 Action Pattern

這是我最常嘮叨的一點:「Controller 只是交通警察,不是法官。

Controller 的職責應該僅限於:接收請求、驗證資料(交給 Form Request)、呼叫邏輯層、回傳回應。絕對不要在 Controller 裡面寫複雜的 `if-else` 商業邏輯或是直接操作 Eloquent 的複雜查詢。

在 Laravel 10 中,我們可以更靈活地使用 Service Pattern 或是近年很流行的 Action Pattern (單一職責類別)。

  • Service Pattern: 適合處理一整組相關的邏輯(例如 UserService 包含註冊、更新、刪除)。
  • Action Pattern: 適合單一且複雜的業務邏輯(例如 PublishArticleAction)。

這樣做的好處是,當你以後需要透過 CLI (Artisan Command) 或 API 觸發同樣的邏輯時,你不需要複製貼上程式碼,只要注入這個 Service 或 Action 即可。

3. Form Request 是你的第一道防線

拜託,別再 Controller 裡面寫 $request->validate([...]) 了。這會讓你的 Controller 變得很髒。

使用 Laravel 的 Form Request 功能,將驗證邏輯獨立出來。這不僅讓 Controller 乾淨,還能複用驗證規則。在 Laravel 10 中,你甚至可以在 Form Request 中定義 prepareForValidation 來預處理資料。

// app/Http/Requests/StoreProductRequest.php

public function rules(): array
{
    return [
        'name' => ['required', 'string', 'max:255'],
        'price' => ['required', 'integer', 'min:0'],
        'status' => ['required', Rule::in(ProductStatus::cases())], // 善用 PHP 8.1 Enums
    ];
}

4. 善用 Enums (列舉) 取代魔法數字

如果你的程式碼充滿了 if ($status == 1)if ($type == 'admin'),請立刻停下來。這種「魔法數字」或「魔法字串」是維護的惡夢。

PHP 8.1 引入的 Enum 是 Laravel 10 開發者的好朋友。你可以結合 Laravel 的 Model Casting,讓資料庫存的是整數或字串,但在程式碼中操作的是強型別的 Enum 物件。

// app/Enums/OrderStatus.php
enum OrderStatus: string
{
    case PENDING = 'pending';
    case PAID = 'paid';
    case SHIPPED = 'shipped';
}

// 在 Model 中使用
protected $casts = [
    'status' => OrderStatus::class,
];

// 在程式邏輯中
if ($order->status === OrderStatus::PAID) {
    // 做些什麼
}

5. 模型 (Model) 應該要「瘦」還是「胖」?

這是一個哲學問題。但我建議 Model 應該包含與資料庫交互的邏輯(如 Scopes, Accessors, Mutators, Relations),但不應該包含「商業流程邏輯」。

善用 Local Scopes 來封裝常用的查詢條件,讓你的查詢語句像英文句子一樣易讀。

糟糕的查詢寫法:

$users = User::where('active', 1)
             ->where('created_at', '>', now()->subDays(7))
             ->orderBy('created_at', 'desc')
             ->get();

優雅的 Scope 寫法:

// User Model 內定義 Scope
public function scopeActive(Builder $query): void { ... }
public function scopeRecent(Builder $query): void { ... }

// 使用時
$users = User::active()->recent()->get();

6. 依賴注入 (Dependency Injection) vs. Facades

Laravel 的 Facades (如 File::put(), Route::get()) 非常方便,但也常被批評難以測試。雖然 Laravel 10 的 Facades 其實很容易 Mock,但在編寫核心商業邏輯(Service/Action)時,我更推薦使用 Constructor Injection (建構子注入)

這能清楚地列出該類別的依賴關係,並利用 PHP 8 的 Constructor Property Promotion 讓程式碼更簡潔。

class ProcessOrderAction
{
    public function __construct(
        protected PaymentGateway $payment,
        protected InventoryService $inventory
    ) {}

    public function execute(Order $order)
    {
        // ...
    }
}

結論:架構是為了未來的自己

寫程式最爽的時刻不是寫完的那一刻,而是三個月後你回來看這段程式碼,發現你依然看得懂,而且能在一分鐘內加上新功能。Laravel 10 提供了非常棒的工具讓我們實踐 Clean Code,重點在於你是否願意在初期多花一點時間去設計架構。

別讓你的專案變成技術債炸彈,從今天開始重構吧!

延伸閱讀

不想讓你的 Laravel 專案變成難以維護的怪獸?

無論是架構規劃、效能優化,還是技術債重構,浪花科技的資深團隊都能為您提供最專業的技術支援。讓我們幫您打造穩固、可擴展的數位基石。

立即填寫表單聯繫我們
// FAQ

常見問題

Laravel 專案中為什麼建議加上參數型別與回傳型別宣告?
Laravel 10 的應用骨架已全面擁抱 PHP 的型別系統。加上型別宣告能讓程式碼剛性更強,減少因 null 或錯誤資料型態造成的隱形 Bug,同時讓 IDE 更好地輔助開發,並使程式碼本身即文件,從簽名就能看出輸入輸出。
Controller 變得肥大時該如何重構?
Controller 的職責應僅限於接收請求、交由 Form Request 驗證、呼叫邏輯層、回傳回應,不應寫複雜的商業邏輯。可將邏輯抽到 Service Pattern(封裝一整組相關邏輯)或 Action Pattern(處理單一且複雜的業務邏輯),日後透過 CLI 或 API 觸發同樣邏輯時就能直接注入重用,不必複製貼上。
Laravel 開發中該用 Enum 取代魔法數字嗎?
建議使用。程式碼中充斥 if (status == 1) 這類魔法數字或魔法字串是維護惡夢。可用 PHP 8.1 的 Enum 搭配 Laravel 的 Model Casting,讓資料庫存整數或字串、但程式中操作的是強型別 Enum 物件,例如以 OrderStatus::PAID 取代數字比對。
Laravel 10 還需要使用 Repository Pattern 嗎?
取決於專案規模。中小型專案直接使用 Eloquent Model 通常已足夠高效直觀,Repository Pattern 往往增加不必要的抽象層。除非有明確需求(例如未來可能更換資料庫或需要極嚴格的單元測試隔離),否則 Service Layer 搭配 Eloquent 通常是更實用的選擇。
在核心商業邏輯中,該用 Dependency Injection 還是 Facade?
Facade 很方便,且在 Laravel 中其實也容易 Mock,但編寫核心商業邏輯的 Service 或 Action 時,更推薦使用建構子注入。它能清楚列出該類別的依賴關係,並可搭配 PHP 8 的 Constructor Property Promotion 讓程式碼更簡潔。
~/roamer-tech/newsletter // FREE
// newsletter

訂閱免費電子報

把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。

$
// final.exec()

準備好讓你的網站開始為你工作了嗎?