2025年6月29日 星期日

用 Laravel 打造企業級 SaaS:模組化單體與微服務的完美結合

用 Laravel 打造企業級 SaaS:模組化單體與微服務的完美結合

身為一名資深 Laravel 開發者,您是否曾想過,如何將專案從單純的 CRUD 應用,提升到具備企業級擴展性、易於維護的 SaaS 平台?這篇文章,將為您揭示一個兼具開發效率與卓越效能的 「模組化單體」(Modular Monolith)架構。

我們將透過一個完整的「餐廳 SaaS 系統」實戰案例,展示核心技術選型與實作細節,助您在面試或專案中,展現超越凡響的架構思維。

您可以透過以下 GitHub 連結檢閱本專案的原始碼:https://github.com/BpsEason/modular-dining-system.git

為何如此選擇?技術棧的優雅協奏

拋開「非單體即微服務」的二元對立思維,我們讓不同的技術棧各司其職。

  • Laravel: 專注處理核心業務邏輯、資料庫操作、RBAC 權限與多租戶管理。它強大的生態系統是構建穩健後台服務的最佳夥伴。

  • FastAPI: 專注於 I/O 密集型計算密集型任務,例如本專案的個性化推薦引擎。Python 的非同步特性(async/await)使其在高併發場景下表現卓越,完美彌補了 PHP 在此領域的不足。

透過 Docker Compose,我們將這兩種技術無縫整合,讓開發、測試與部署流程極致流暢。

第一步:告別單體,擁抱模組化骨架

傳統 Laravel 專案的程式碼常混雜在 app/Http/Controllers 中,難以維護。我們的解決方案,是使用 nwidart/laravel-modules 套件來建立獨立的模組化結構。

在專案的啟動腳本中,我們定義了清晰的資料夾結構,讓每個模組(如 CustomerProfile, Marketing, PosCore)都有自己的 ControllersRoutesModels

Bash
DIRS=(
    "laravel-app/modules/CustomerProfile/Http/Controllers"
    "laravel-app/modules/LoyaltyProgram/Http/Controllers"
    "laravel-app/modules/Marketing/Http/Controllers"
    "laravel-app/modules/PosCore/Http/Controllers"
    "laravel-app/routes"
    # ... 其他目錄
)

for dir in "${DIRS[@]}"; do
    mkdir -p "$dir"
done

關鍵提示: 自動化腳本確保團隊遵循統一架構,從專案初始化就建立高水準的開發規範。

第二步:跨越語言邊界:從 Laravel 呼叫 FastAPI

真正的亮點在此!當 Marketing 模組需要發送個性化推播通知時,它不會自己處理複雜的推薦邏輯,而是優雅地將任務交給 FastAPI 服務.

瞧瞧程式碼如何協作:

  1. Laravel 端(Marketing 模組):

    使用 Http Facade 進行服務呼叫。我們在 NotificationController.php 中將任務推入隊列,確保請求能即時回應。

    PHP
    // laravel-app/modules/Marketing/Http/Controllers/NotificationController.php
    public function send(Request $request)
    {
        // ... 驗證邏輯
        // 將發送通知的任務推入隊列
        SendPushNotification::dispatch($validated['message'], $validated['user_id']);
    
        return response()->json(['message' => 'Notification queued successfully.']);
    }
    
  2. FastAPI 端(Recommendation 服務):

    main.py 啟動一個獨立服務,提供高效的推薦 API。

    Python
    # fastapi-service/main.py
    @app.get("/api/v1/recommendations/{customer_id}", response_model=RecommendationResponse)
    def get_recommendations(customer_id: int):
        # ... 推薦邏輯:讀取 Redis 快取或 MySQL 資料
        recommended_items = ...
        return RecommendationResponse(items=recommended_items, strategy="Collaborative Filtering (KNN)")
    
  3. 隊列中的實際呼叫:

    在 SendPushNotification Job 裡,我們使用 Laravel 的 Http Client 呼叫 FastAPI, 再將推薦結果組合成推播訊息。

    PHP
    // laravel-app/app/Jobs/SendPushNotification.php
    public function handle(): void
    {
        // 呼叫 FastAPI 推薦服務
        $recommendations = Http::get("http://fastapi:8000/api/v1/recommendations/{$this->userId}")->json();
    
        $message = "Try our recommended {$recommendations['items'][0]['name']}!";
    
        // ... 使用 LINE/SMS API 發送通知
        Http::withHeaders(...)->post(env('PUSH_SERVICE_ENDPOINT'), [
            'user_id' => $this->userId,
            'message' => $message,
        ]);
    }
    

關鍵提示: 透過將耗時的 API 呼叫放入隊列,我們徹底避免了阻塞主執行緒,大幅提升使用者體驗。

第三步:為失敗而建:強韌的任務處理

專業系統不僅為成功而設計,更要為失敗做好準備。如果推播服務失敗,我們的 SendPushNotification Job 會觸發以下機制:

  • 自動重試: $this->tries = 3; 讓任務自動重試三次,大幅提高成功率。

  • 錯誤日誌: 若重試仍失敗,我們會將通知失敗的紀錄寫入 notification_logs 資料表,方便後續追蹤與分析。

PHP
// laravel-app/app/Jobs/SendPushNotification.php
public function handle(): void
{
    try {
        // ... 發送通知的邏輯
    } catch (Throwable $e) {
        // 失敗時記錄到資料庫
        NotificationLog::create([
            'user_id' => $this->userId,
            'message' => $this->message,
            'status' => 'failed',
            'error_message' => $e->getMessage(),
        ]);
        
        // 如果還沒達到最大重試次數,拋出例外以觸發重試
        if ($this->attempts() < $this->tries) {
            throw $e;
        }
    }
}

關鍵提示: 這種設計展現了對 系統韌性 (Resilience)可觀測性 (Observability) 的深刻理解。

第四步:從源頭開始,確保 SaaS 安全

在多租戶 SaaS 架構中,資料隔離是生命線。我們結合 spatie/laravel-permission 與自定義 Middleware,構築了堅固的權限與租戶管理防線。

  • 細粒度 RBAC: 我們定義了 adminmanagerstaff 等角色,並賦予精細的權限,如 customer.readorder.create

  • 強制租戶隔離: 透過 CheckTenant Middleware,強制每個 API 請求都必須攜帶有效的 X-Tenant-ID Header。

PHP
// laravel-app/app/Http/Middleware/CheckTenant.php
public function handle(Request $request, Closure $next): Response
{
    $tenantId = $request->header('X-Tenant-ID');
    if (! $tenantId || ! auth()->user()->tenant_id || (int) $tenantId !== auth()->user()->tenant_id) {
        return response()->json(['message' => 'Unauthorized or invalid tenant.'], 403);
    }
    return $next($request);
}

關鍵提示: 這種強制性的驗證機制,從根本上確保了不同租戶的資料在邏輯上完全隔離。

第五步:一鍵部署:DevOps 的終極藝術

資深工程師的價值,在於將個人能力轉化為團隊的效率。我們透過 GitHub Actions 實現全自動化的 CI/CD 流程。

  • 自動化測試: 程式碼提交後,自動執行 Laravel (PHPUnit)、Vue (Vitest) 和 FastAPI (Pytest) 的所有測試。

  • 品質保證: 測試結果會生成覆蓋率報告,並上傳至 Codecov,確保程式碼品質。

  • 無縫部署: 只要程式碼推送到 main 分支,GitHub Actions 就會自動建置 Docker 映像檔並推送到 Docker Hub,實現零停機更新。

關鍵提示: 這種流程消除了手動部署的風險,將「從程式碼提交到生產部署」變成無縫銜接的自動化藝術。

沒有留言:

張貼留言

熱門文章