從零到一打造多租戶 SaaS 平台:一個 Laravel + Vue.js 的餐飲電商實戰案例
嗨!各位 Laravel 的同好們,大家好!
你是否曾經想過,如何用 Laravel 打造一個可以同時服務多個客戶,又各自擁有獨立資料的 SaaS (Software as a Service) 平台?
今天,我想跟大家分享一個我最近整理的開源專案 ECommerceTenancy,它是一個專為餐飲業設計的多租戶電商平台原型。別看它名字有點硬,它可是個麻雀雖小,五臟俱全的寶藏專案。
這個專案不只教你 CRUD,更帶你深入探討:
多租戶架構:用子域名動態切換客戶資料,超帥氣!
前後端協作:如何用 WebSocket 實現即時叫號系統?
實戰監控:用 Prometheus 和 Grafana 讓你的老闆一眼看出 API 狀況。
準備好了嗎?讓我們一起來趟技術深度之旅吧!
第一站:架構設計的腦力激盪 - 為什麼選擇 Row-based 多租戶?
在設計多租戶架構時,我們有幾種選擇:
Schema-based:每個租戶一個 Schema。
Database-based:每個租戶一個資料庫。
Row-based:所有租戶共用資料表,用
tenant_id
欄位區分。
在這個專案中,我選擇了 Row-based。為什麼呢?
成本效益:省錢!所有租戶共用一個資料庫實例,部署和維護成本低。
部署簡單:一套程式碼、一個資料庫,部署流程超簡潔。
當然,這也帶來了挑戰:如何確保資料不會「串門子」?
我的解法是:全域作用域 (Global Scope) + Middleware。
Middleware 的魔術:
當使用者透過 tenanta.localhost 訪問時,我會用一個 Laravel Middleware 擷取出 tenanta 這個租戶識別碼。
PHP// 類似這樣的邏輯,在 Middleware 中解析子域名 $tenantId = app('tenant_resolver')->resolveFromSubdomain($request->getHost()); // 將租戶 ID 設為全域可存取 TenantContext::setTenantId($tenantId);
Eloquent Global Scope 的神助攻:
我為所有需要租戶隔離的 Eloquent Model(像是 Product、Order、Table)都加上了一個 TenantScope。
PHP// 在你的 App\Models\Product.php 中 protected static function booted() { static::addGlobalScope(new TenantScope()); } // TenantScope.php 內部大概長這樣 public function apply(Builder $builder, Model $model) { if ($tenantId = TenantContext::getTenantId()) { $builder->where('tenant_id', $tenantId); } }
這樣一來,無論你下什麼查詢,Laravel 都會自動幫你加上
WHERE tenant_id = 'xxx'
的條件,從根源上確保了資料隔離,再也不用擔心手滑忘記加條件啦!
第二站:前後端即時串接 - 打造即時叫號系統
餐飲業最常見的痛點之一就是叫號。客人坐在位子上等,如何讓他們即時看到叫號進度?
ECommerceTenancy 採用了 Laravel Echo + WebSocket 來實現這個即時叫號功能。
後端事件觸發:
當後端新增或更新一個叫號時 (例如:服務生在後台點擊「下一個號碼」),我們就觸發一個 Laravel Event。
PHP// 在你的 QueueController.php 中 // 處理叫號更新的邏輯... // ... event(new QueueUpdated($newQueueNumber));
後端廣播:
這個 QueueUpdated 事件會被 BroadcastServiceProvider 捕捉,並透過 WebSocket 服務(例如 Pusher 或 laravel-websockets)廣播出去。
PHP// 在你的 QueueUpdated.php Event 中 use Illuminate\Broadcasting\Channel; // ... public function broadcastOn(): Channel { // 讓所有人都監聽同一個公開頻道 return new Channel('public.queue.updates'); }
前端即時監聽:
在 Vue.js 前端,我們的 QueueDisplay.vue 組件會使用 laravel-echo 監聽這個頻道。
JavaScript// frontend/src/components/QueueDisplay.vue import Echo from 'laravel-echo'; // ... mounted() { window.Echo.channel('public.queue.updates') .listen('QueueUpdated', (e) => { console.log('叫號更新了!', e.queueNumber); // 更新 Vue 元件的 data,讓畫面即時渲染 this.currentNumber = e.queueNumber; }); }
這個流程簡潔、高效,將後端業務邏輯與前端 UI 更新完美解耦,讓開發變得優雅又快樂。
第三站:運維工程師的救星 - 監控整合
身為一個資深工程師,寫出高品質的程式碼只是基本,讓系統穩定運行才是王道。
ECommerceTenancy 整合了 Prometheus 和 Grafana,讓你輕鬆掌握系統脈動。
我寫了一個 RecordMetrics
Middleware,它會在每個 API 請求結束時,自動將請求數據傳送給 Prometheus。
// backend/app/Http/Middleware/RecordMetrics.php
// 核心邏輯就是捕捉請求方法、路徑、狀態碼,並記錄到 Prometheus
$counter = $registry->getOrRegisterCounter(
'ecommerce_platform',
'http_requests_total',
'Total HTTP requests to the application',
['method', 'endpoint', 'status_code']
);
$counter->inc([$request->method(), $request->path(), (string) $response->getStatusCode()]);
透過 Docker Compose,我們一鍵啟動 Prometheus 和 Grafana 服務。你只需要在 Grafana 裡面設定一個 Dashboard,就可以看到每個 API 的請求量、錯誤率、延遲時間等關鍵指標。
這不僅能幫助你快速排查問題,還能為未來的系統優化提供數據依據。
總結
ECommerceTenancy 專案是一個濃縮了現代 Laravel 開發精華的實戰案例。它涵蓋了多租戶架構、即時通訊、監控整合、JWT 認證、RBAC 權限控制、CI/CD 等多個領域。
如果你是 Laravel 工程師,希望提升自己的架構設計能力,或是正在尋找一個可以應用到實際專案中的完整範本,我強烈推薦你下載程式碼、親自動手 run 起來!
專案傳送門:
(請替換為您的 repo 連結) ECommerceTenancy GitHub Repo
希望這篇文章對你有所啟發。如果你有任何問題或想法,歡迎留言交流!我們下次見!
沒有留言:
張貼留言