你的 Laravel 後台是『義大利麵』還是『積木城堡』?終極 Admin 架構設計指南,打造可傳承的程式碼藝術品

2025/12/25 | Laravel技術分享, 全端與程式開發, 技術教學資源

從義大利麵到積木城堡:Laravel Admin 終極架構重構指南

你的 Laravel 後台是不是也陷入「肥控制器」的泥沼,變成一團難以收拾的「義大利麵」程式碼?資深工程師 Eric 帶領你啟動一場關鍵的架構重構之旅!我們將運用 Form Requests 進行驗證瘦身、導入 Service Layer 集中業務核心,並透過 Repository Pattern 實現資料庫解耦。這不只是修補 Bug,更是將你的專案升級為結構清晰、高度可測試且易於維護的「積木城堡」。別讓雜亂的架構成為未來擴展的負擔,立即學習這份專業指南,打造一份能讓你和團隊感到驕傲的程式碼藝術品!

需要專業協助?

聯絡浪花專案團隊 →

你的 Laravel 後台是『義大利麵』還是『積木城堡』?終極 Admin 架構設計指南,打造可傳承的程式碼藝術品

哈囉,我是浪花科技的資深工程師 Eric。寫了這麼多年的 Code,我看過太多一開始光鮮亮麗,後來卻變成『義大利麵』的專案。程式碼全部攪和在一起,動一髮而牽全身,別說交接了,有時候連幾個月後的自己都看不懂。尤其在 Laravel Admin 後台的開發中,這種情況更是屢見不鮮。

很多開發者(包括年輕時的我)剛接觸 Laravel 時,都會愛上它的優雅與快速。一個 `artisan make:controller` 指令下去,一個 Resource Controller 就建好了,然後就把所有的商業邏輯、資料庫操作、資料驗證…通通塞進 Controller 的七個方法裡。專案小的時候,這樣做很快,很爽。但隨著功能越來越複雜,你的 Controller 就會像吹氣球一樣,迅速膨脹成一隻難以駕馭的怪獸——我們稱之為「肥控制器」(Fat Controller)。

今天,我不是要來嚇唬你,而是想帶你走一趟 Laravel 後台架構的重構之旅。我們會從失控的義大利麵程式碼開始,一步步地拆解、重組,最終打造出一個像樂高積木一樣,結構清晰、易於維護、可擴展的『積木城堡』。這不只是寫出能動的 Code,更是打造一份能讓你(和你的團隊)在未來感到驕傲的藝術品。

第一站:MVC 的美麗與哀愁 – 為什麼 Controller 會失控?

我們先來看看一個典型的『肥控制器』案例。假設我們在開發一個商品管理的後台,`ProductController` 的 `store` 方法可能會長這樣:


<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Services\NotificationService; // 假設有個通知服務

class ProductController extends Controller
{
    public function store(Request $request)
    {
        // 1. 資料驗證
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|max:255',
            'sku' => 'required|string|unique:products,sku',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0',
        ]);

        if ($validator->fails()) {
            return redirect()->back()->withErrors($validator)->withInput();
        }

        // 2. 核心商業邏輯
        $product = new Product();
        $product->name = $request->input('name');
        $product->sku = $request->input('sku');
        $product->price = $request->input('price');
        $product->stock = $request->input('stock');
        $product->is_active = true; // 預設為啟用

        // 如果有上傳圖片,處理圖片
        if ($request->hasFile('image')) {
            $path = $request->file('image')->store('products', 'public');
            $product->image_url = $path;
        }

        $product->save();

        // 3. 觸發其他商業行為 (例如:發送通知)
        NotificationService::sendAdminNotification('新商品已建立:' . $product->name);

        // 4. 回傳 HTTP Response
        return redirect()->route('products.index')->with('success', '商品建立成功!');
    }
}

看起來好像沒什麼問題,對吧?但仔細想想,這個 `store` 方法做了太多事了。它違反了軟體設計中的「單一職責原則」(Single Responsibility Principle, SRP)。

  • 難以閱讀:所有的邏輯都混在一起,你需要從頭看到尾才能理解完整的流程。
  • 難以測試:你怎麼對這段程式碼寫單元測試?你必須模擬一個完整的 HTTP Request,非常麻煩。你想單獨測試「圖片上傳邏輯」或「通知邏輯」嗎?幾乎不可能。
  • 程式碼重複:`update` 方法是不是也要寫一套幾乎一樣的驗證規則?如果新增一個 API 端點來建立商品,是不是又要複製貼上一次?
  • 職責不清:Controller 的核心職責應該是接收 HTTP Request,並回傳 HTTP Response。它應該是個交通警察,而不是自己下去蓋房子。

這就是我們需要動手術的原因。接下來,我們一步步把它拆解開來。

第二站:減肥手術第一刀 – 善用 Form Requests 拆分驗證邏輯

Laravel 提供了一個非常優雅的工具來處理驗證:`FormRequest`。它可以把驗證邏輯從 Controller 中完全抽離出來。

我們先用 artisan 指令建立一個 `FormRequest`:

php artisan make:request StoreProductRequest

然後,把驗證邏輯搬進去 `app/Http/Requests/StoreProductRequest.php`:


<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreProductRequest extends FormRequest
{
    public function authorize()
    {
        // 這裡可以寫權限驗證邏輯,例如:判斷當前使用者是否有權限建立商品
        return true; // 暫時先設為 true
    }

    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'sku' => 'required|string|unique:products,sku',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0',
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
        ];
    }
}

現在,我們的 Controller 就可以瘦身了。只需要在 `store` 方法中,將 `Illuminate\Http\Request` 型別提示換成我們剛建立的 `StoreProductRequest`。


// ...
use App\Http\Requests\StoreProductRequest;

class ProductController extends Controller
{
    public function store(StoreProductRequest $request)
    {
        // 驗證邏輯已經自動執行!如果驗證失敗,Laravel 會自動跳轉回去。
        // 你可以直接使用 $request->validated() 來取得已驗證的資料。
        $validatedData = $request->validated();

        // ... 剩下的商業邏輯
    }
}

你看,Controller 是不是瞬間清爽多了?驗證邏輯被封裝到專屬的 class 裡,不僅 Controller 變乾淨了,這個 `StoreProductRequest` 還可以被重複使用。這就像是為你的 Controller 請了一個專業的保鑣,閒雜人等(無效資料)一律擋在門外,Controller 只需要專心接待貴賓就好。

第三站:打造你的『業務邏輯中樞』- Service Layer 的導入

驗證問題解決了,但 Controller 裡面還是有一大堆商業邏輯。這時候,就該輪到「服務層」(Service Layer)登場了。

Service Layer 的概念很簡單:將核心的商業邏輯(Application Logic)從 Controller 中抽離,封裝到獨立的 Service Class 中。Controller 的職責就只剩下:

  1. 接收 Request(已經被 FormRequest 清理乾淨了)。
  2. 呼叫對應的 Service 方法來處理業務。
  3. 根據 Service 的回傳結果,決定要回傳哪個 Response。

我們來建立一個 `ProductService`:


<?php

namespace App\Services;

use App\Models\Product;
use App\Services\NotificationService;
use Illuminate\Support\Facades\Storage;

class ProductService
{
    public function createProduct(array $data): Product
    {
        $product = new Product();
        $product->fill($data); // 使用 Mass Assignment
        $product->is_active = true;

        if (isset($data['image'])) {
            $path = $data['image']->store('products', 'public');
            $product->image_url = Storage::url($path);
        }

        $product->save();

        // 觸發通知
        NotificationService::sendAdminNotification('新商品已建立:' . $product->name);

        return $product;
    }
}

接著,我們在 Controller 中透過「依賴注入」(Dependency Injection)來使用這個 Service:


<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreProductRequest;
use App\Services\ProductService;

class ProductController extends Controller
{
    protected $productService;

    public function __construct(ProductService $productService)
    {
        $this->productService = $productService;
    }

    public function store(StoreProductRequest $request)
    {
        $this->productService->createProduct($request->validated());

        return redirect()->route('products.index')->with('success', '商品建立成功!');
    }
}

工程師的小囉嗦時間:看到這裡,你可能會覺得「有必要搞這麼複雜嗎?」。相信我,絕對有必要。當你的業務邏輯越來越複雜,例如「建立商品後,需要同步到 ERP 系統」、「需要更新會員的購買記錄」、「需要觸發一個複雜的行銷活動」,這些邏輯都應該放在 `ProductService` 裡。Controller 始終保持乾淨,它完全不需要知道背後發生了什麼驚天動地的大事,這就是「關注點分離」(Separation of Concerns)的精髓。

第四站:解耦資料庫操作 – Repository Pattern 的優雅

我們的架構已經改善很多了,但還有優化的空間。在 `ProductService` 中,我們直接使用了 `Product` 這個 Eloquent Model。這在多數情況下沒問題,但如果我們想進一步提高程式碼的彈性和可測試性,「倉儲模式」(Repository Pattern)就是你的好朋友。

Repository Pattern 的核心思想是建立一個資料存取的中介層。Service 不再直接跟 Eloquent Model 對話,而是透過 Repository 來操作資料。這樣做的好處是:

  • 易於測試:在測試 Service 時,你可以輕易地用一個「假的」(Mock)Repository 來取代真實的資料庫操作。
  • 資料來源抽換:如果未來你的商品資料來源不只是 MySQL,可能還有一部分來自外部 API 或 Redis,你只需要建立一個新的 Repository 來實現同樣的介面,Service 層的程式碼完全不用動。

實作上,我們通常會先定義一個 Interface(介面),再建立一個 Class 來實現它。

1. 建立 Interface `app/Repositories/Interfaces/ProductRepositoryInterface.php`


<?php

namespace App\Repositories\Interfaces;

use App\Models\Product;

interface ProductRepositoryInterface
{
    public function create(array $data): Product;
}

2. 建立實現 `app/Repositories/Eloquent/ProductRepository.php`


<?php

namespace App\Repositories\Eloquent;

use App\Models\Product;
use App\Repositories\Interfaces\ProductRepositoryInterface;

class ProductRepository implements ProductRepositoryInterface
{
    public function create(array $data): Product
    {
        return Product::create($data);
    }
}

3. 在 `AppServiceProvider` 中綁定 Interface 和實現


// app/Providers/AppServiceProvider.php
use App\Repositories\Interfaces\ProductRepositoryInterface;
use App\Repositories\Eloquent\ProductRepository;

public function register()
{
    $this->app->bind(
        ProductRepositoryInterface::class,
        ProductRepository::class
    );
}

最後,改造我們的 `ProductService`,讓它依賴 `ProductRepositoryInterface` 而不是 `Product` Model。


<?php

namespace App\Services;

use App\Models\Product;
use App\Repositories\Interfaces\ProductRepositoryInterface;
use Illuminate\Support\Facades\Storage;

class ProductService
{
    protected $productRepository;
    protected $notificationService; // 假設 NotificationService 也被注入

    public function __construct(ProductRepositoryInterface $productRepository, NotificationService $notificationService)
    {
        $this->productRepository = $productRepository;
        $this->notificationService = $notificationService;
    }

    public function createProduct(array $data): Product
    {
        $productData = $data;
        $productData['is_active'] = true;

        if (isset($data['image'])) {
            $path = $data['image']->store('products', 'public');
            $productData['image_url'] = Storage::url($path);
            unset($productData['image']);
        }

        $product = $this->productRepository->create($productData);

        $this->notificationService->sendAdminNotification('新商品已建立:' . $product->name);

        return $product;
    }
}

到這裡,我們的架構已經非常清晰了。每一層都有自己明確的職責,就像一條分工精細的工廠生產線。

終點站:組建你的『樂高城堡』- 完整的 Laravel Admin 架構

讓我們回顧一下一個 Request 的完整旅程:

  1. HTTP Request 進入路由(Route)。
  2. 路由導向 `ProductController` 的 `store` 方法。
  3. `StoreProductRequest` 攔截請求,進行權限和資料驗證。
  4. 驗證通過後,`ProductController` 呼叫 `ProductService` 的 `createProduct` 方法,並傳入驗證過的資料。
  5. `ProductService` 處理核心商業邏輯(如處理圖片、設定預設值),然後呼叫 `ProductRepository` 來將資料存入資料庫。
  6. `ProductRepository` 執行 Eloquent 操作,將資料寫入 `products` 資料表。
  7. 資料庫操作完成後,`ProductService` 可能會再呼叫其他 Service(如 `NotificationService`)。
  8. `ProductService` 將建立好的 Product Model 回傳給 `ProductController`。
  9. `ProductController` 根據結果,回傳一個重導向的 HTTP Response。

這個結構帶來的好處是巨大的:

  • 高可維護性:想修改驗證規則?去 `FormRequest`。想修改商業邏輯?去 `Service`。想修改資料庫查詢?去 `Repository`。一切都井井有條。
  • 高可測試性:每一層都可以獨立進行單元測試,確保程式碼的品質。
  • 高可擴展性:未來新增功能時,你只需要組合或擴展現有的 Service 和 Repository,而不用去動到那坨巨大的 Controller。
  • 團隊協作:職責清晰,團隊成員可以分工負責不同的層級,減少互相干擾。

相關閱讀

結論:不只是寫 Code,更是對未來的投資

從一團混亂的義大利麵,到結構清晰的積木城堡,這趟旅程的核心精神在於「拆解」與「分層」。好的軟體架構,就像蓋房子前畫好的藍圖,它決定了這棟建築能蓋多高、能用多久。

或許一開始你會覺得多寫了幾個檔案、多了幾層抽象很麻煩,但請相信我,這是在為你的未來投資。當專案長大、需求變更時,你會感謝當初那個願意多花一點時間把地基打穩的自己。

如果你正在為雜亂的後台架構所苦,或是準備打造一個能支撐未來業務擴展的強大系統,卻不知從何下手,浪花科技的團隊擁有豐富的 Laravel 專案架構經驗。我們樂於協助你打造出堅固、優雅且可傳承的數位產品。歡迎點擊這裡與我們聯繫,讓我們聊聊你的專案吧!

常見問題 (FAQ)

Q1: Service Layer 和 Repository Pattern 一定要一起用嗎?

不一定。這取決於專案的複雜度。對於中小型專案,只導入 Service Layer 來分離商業邏輯,並在 Service 中直接使用 Eloquent Model 也是一個非常常見且有效的做法。當你的資料庫查詢變得非常複雜,或是有抽換資料來源的需求時,再導入 Repository Pattern 也不遲。架構是演化而來的,不需要一開始就過度設計。

Q2: 什麼時候我的專案才『需要』這麼複雜的架構?

一個好的判斷點是:當你發現 Controller 的方法超過一頁螢幕、開始出現重複的邏輯、或者你覺得要為某個功能寫測試變得很困難時,就是重構和分層的好時機。我的建議是,對於任何預期會長期維護和迭代的專案,從一開始就建立 Service Layer 和使用 FormRequest 是個好習慣。

Q3: 使用這些模式會不會讓開發速度變慢?

短期來看,建立這些檔案和分層會增加一些初始的開發時間。但長期來看,它會大大加快你的開發和維護速度。因為程式碼結構清晰,新增功能、除錯、或交接給新成員時,效率會高非常多。這是一種「先苦後甘」的投資。

Q4: Laravel 內建的 Resource Controller 和這個架構有衝突嗎?

完全沒有衝突!Resource Controller 提供了一個很好的 RESTful 方法論骨架(index, create, store, show, edit, update, destroy)。我們今天討論的架構設計,正是在這個骨架之內,將每個方法的「實作」變得更乾淨、更有組織。你可以繼續使用 Resource Controller,但把內部的邏輯拆分到 FormRequest、Service 和 Repository 中。

 
立即諮詢,索取免費1年網站保固