Laravel 12 SaaS 开发入门:从架构设计到常用框架全攻略

摘要

本文深入探讨 Laravel 12 在 SaaS(Software as a Service)开发中的应用,包括 SaaS 架构设计、多租户实现方案、订阅管理系统、billing 集成、常用框架和工具,以及部署和扩展策略。通过详细的代码示例和架构图,帮助开发者掌握 Laravel 12 构建 SaaS 应用的核心技术,从入门到精通 Laravel 12 SaaS 开发。

1. Laravel 12 SaaS 开发概述

1.1 什么是 SaaS

SaaS 是一种软件交付模式,软件提供商将应用托管在云端,通过订阅模式向客户提供服务。SaaS 具有以下特点:

  • 按需订阅:客户根据需求选择订阅计划
  • 云端部署:软件托管在云服务器上,无需客户本地安装
  • 多租户架构:单个应用实例服务多个客户
  • 自动更新:提供商负责软件更新和维护
  • 按使用付费:通常基于用户数量、使用量或功能模块收费

1.2 Laravel 12 适合 SaaS 开发的原因

Laravel 12 是构建 SaaS 应用的理想选择,主要原因包括:

  • 优雅的架构:清晰的分层架构,便于扩展和维护
  • 强大的生态系统:丰富的扩展包和工具
  • 多租户支持:易于实现多租户架构
  • API 友好:内置强大的 API 支持
  • 认证和授权:完善的用户认证和权限管理
  • 队列系统:支持后台任务处理
  • 缓存系统:提高应用性能
  • 测试支持:确保代码质量

2. Laravel 12 SaaS 架构设计

2.1 Laravel 12 单租户 vs 多租户架构

单租户架构

  • 特点:每个客户有独立的应用实例和数据库
  • 优势:数据隔离性好,定制化容易
  • 劣势:部署和维护成本高,资源利用率低

多租户架构

  • 特点:单个应用实例服务多个客户
  • 优势:部署和维护成本低,资源利用率高
  • 劣势:数据隔离性需要额外设计,定制化相对复杂

2.2 Laravel 12 多租户架构设计模式

1. 共享数据库,共享架构

  • 实现方式:所有租户共享同一个数据库,通过 tenant_id 字段区分
  • 优势:简单易实现,成本最低
  • 劣势:数据隔离性差,可能存在性能瓶颈

2. 共享数据库,隔离架构

  • 实现方式:所有租户共享同一个数据库,但每个租户有独立的表前缀或schema
  • 优势:数据隔离性较好,性能适中
  • 劣势:数据库架构复杂,迁移困难

3. 隔离数据库

  • 实现方式:每个租户有独立的数据库
  • 优势:数据隔离性最好,性能最优
  • 劣势:部署和维护成本高,数据库连接管理复杂

2.3 Laravel 12 SaaS 架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌───────────────────────────────────────────────────────────────────────────┐
│ 客户端层 │
├─────────────────┬─────────────────┬─────────────────┬─────────────────┤
│ Web 浏览器 │ 移动应用 │ API 客户端 │ 第三方集成 │
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘

┌───────────────────────────────────────────────────────────────────────────┐
│ 接入层 │
├───────────────────────────────────────────────────────────────────────────┤
│ API 网关(Laravel + Octane) │
└───────────────────────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────────────────────┐
│ 应用层 │
├─────────────────┬─────────────────┬─────────────────┬─────────────────┤
│ 核心服务 │ 认证服务 │ 订阅服务 │ billing服务 │
│ (Laravel 12) │ (Laravel + │ (Laravel + │ (Laravel + │
│ │ Sanctum) │ Cashier) │ Stripe/PayPal)│
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘

┌───────────────────────────────────────────────────────────────────────────┐
│ 数据层 │
├─────────────────┬─────────────────┬─────────────────┬─────────────────┤
│ 多租户数据库 │ Redis缓存 │ 文件存储 │ 搜索服务 │
│ (MySQL/PostgreSQL) │ (缓存、会话) │ (S3/本地) │ (Algolia/Elastic)│
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘

3. Laravel 12 多租户实现

3.1 Laravel 12 基于中间件的多租户实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// app/Http/Middleware/IdentifyTenant.php
namespace App\Http\Middleware;

use Closure;
use App\Models\Tenant;

class IdentifyTenant
{
public function handle($request, Closure $next)
{
// 通过域名识别租户
$host = $request->getHost();
$tenant = Tenant::where('domain', $host)->first();

if (!$tenant) {
// 通过子域名识别租户
$parts = explode('.', $host);
if (count($parts) > 2) {
$subdomain = $parts[0];
$tenant = Tenant::where('subdomain', $subdomain)->first();
}
}

if (!$tenant) {
abort(404, 'Tenant not found');
}

// 将租户信息存储到请求中
$request->merge(['tenant' => $tenant]);

// 存储到全局容器
app()->instance('tenant', $tenant);

return $next($request);
}
}

3.2 Laravel 12 多租户数据库连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// app/Providers/TenantServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\DB;

class TenantServiceProvider extends ServiceProvider
{
public function boot()
{
// 注册租户数据库连接
$this->app->resolving('db', function ($db) {
$tenant = $this->app->has('tenant') ? $this->app->get('tenant') : null;

if ($tenant) {
// 共享数据库模式
config(['database.connections.mysql.prefix' => 'tenant_' . $tenant->id . '_']);

// 或者隔离数据库模式
/*
config(['database.connections.mysql.host' => $tenant->db_host]);
config(['database.connections.mysql.database' => $tenant->db_name]);
config(['database.connections.mysql.username' => $tenant->db_username]);
config(['database.connections.mysql.password' => $tenant->db_password]);
*/

// 重新连接数据库
DB::reconnect();
}
});
}
}

3.3 Laravel 12 多租户路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// routes/web.php
Route::middleware(['identify.tenant'])->group(function () {
// 租户相关路由
Route::get('/', function () {
$tenant = request()->tenant;
return view('welcome', ['tenant' => $tenant]);
});

// 其他租户路由
});

// 全局路由(不需要租户识别)
Route::get('/register', [AuthController::class, 'showRegistrationForm']);
Route::post('/register', [AuthController::class, 'register']);
Route::get('/login', [AuthController::class, 'showLoginForm']);
Route::post('/login', [AuthController::class, 'login']);

3.4 Laravel 12 多租户模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// app/Models/Tenant.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Tenant extends Model
{
protected $fillable = [
'name',
'domain',
'subdomain',
'database_name',
'database_username',
'database_password',
'status',
'trial_ends_at',
'subscription_ends_at',
];

public function users()
{
return $this->hasMany(User::class);
}

public function subscriptions()
{
return $this->hasMany(Subscription::class);
}

public function isActive()
{
return $this->status === 'active' &&
($this->subscription_ends_at === null || $this->subscription_ends_at > now());
}
}

// app/Models/User.php
namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
protected $fillable = [
'name',
'email',
'password',
'tenant_id',
'role',
];

public function tenant()
{
return $this->belongsTo(Tenant::class);
}
}

4. Laravel 12 订阅管理系统

4.1 Laravel 12 使用 Cashier 实现订阅管理

Laravel Cashier 是官方提供的订阅管理包,支持 Stripe 和 Paddle 等支付网关。

安装 Cashier

1
2
3
composer require laravel/cashier
php artisan vendor:publish --tag="cashier-migrations"
php artisan migrate

配置 Cashier

1
2
3
4
5
6
7
8
9
10
11
12
13
// config/services.php
return [
// ...
'stripe' => [
'model' => App\Models\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'webhook' => [
'secret' => env('STRIPE_WEBHOOK_SECRET'),
'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
],
],
];

定义订阅计划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// app/Models/Plan.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Plan extends Model
{
protected $fillable = [
'name',
'slug',
'stripe_plan',
'price',
'description',
'features',
'interval', // monthly, yearly
'is_active',
];

protected $casts = [
'features' => 'array',
'is_active' => 'boolean',
];

public function getFeaturesListAttribute()
{
return is_array($this->features) ? $this->features : [];
}
}

订阅管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// app/Http/Controllers/SubscriptionController.php
namespace App\Http\Controllers;

use App\Models\Plan;
use Illuminate\Http\Request;
use Laravel\Cashier\Exceptions\IncompletePayment;

class SubscriptionController extends Controller
{
public function index()
{
$plans = Plan::where('is_active', true)->get();
$user = auth()->user();
$subscription = $user->subscription('default');

return view('subscriptions.index', compact('plans', 'subscription'));
}

public function subscribe(Request $request, Plan $plan)
{
$user = auth()->user();

try {
$user->newSubscription('default', $plan->stripe_plan)
->create($request->payment_method);

return redirect()->route('subscriptions.index')
->with('success', '订阅成功!');
} catch (IncompletePayment $exception) {
return redirect()->route('cashier.payment', [
$exception->payment->id,
'redirect' => route('subscriptions.index'),
]);
}
}

public function cancel()
{
$user = auth()->user();
$user->subscription('default')->cancel();

return redirect()->route('subscriptions.index')
->with('success', '订阅已取消!');
}

public function resume()
{
$user = auth()->user();
$user->subscription('default')->resume();

return redirect()->route('subscriptions.index')
->with('success', '订阅已恢复!');
}
}

4.2 Laravel 12 自定义订阅管理

对于需要更灵活订阅管理的场景,可以实现自定义订阅系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// app/Models/Subscription.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Subscription extends Model
{
protected $fillable = [
'tenant_id',
'plan_id',
'status', // active, cancelled, expired, trial
'starts_at',
'ends_at',
'trial_ends_at',
'payment_method',
'payment_reference',
];

protected $dates = [
'starts_at',
'ends_at',
'trial_ends_at',
];

public function tenant()
{
return $this->belongsTo(Tenant::class);
}

public function plan()
{
return $this->belongsTo(Plan::class);
}

public function isActive()
{
return $this->status === 'active' &&
$this->ends_at && $this->ends_at > now();
}

public function isOnTrial()
{
return $this->status === 'trial' &&
$this->trial_ends_at && $this->trial_ends_at > now();
}
}

5. Laravel 12 SaaS 常用框架和工具

5.1 Laravel 12 核心框架

框架/工具用途优势
Laravel 12核心框架优雅架构,强大生态
Laravel Octane性能优化提高请求处理速度
Laravel SanctumAPI 认证轻量级 API 认证
Laravel PassportOAuth 认证完整的 OAuth2 支持
Laravel Cashier订阅管理集成 Stripe 和 Paddle
Laravel Horizon队列监控监控和管理队列任务
Laravel Telescope调试工具实时监控应用状态
Laravel Scout搜索功能全文搜索支持

5.2 Laravel 12 多租户框架

框架用途特点
Stancl/Multitenancy多租户管理灵活的多租户实现
Tenancy/Framework多租户框架完整的多租户解决方案
Hyn/Multi-tenant多租户支持成熟的多租户扩展

5.3 Laravel 12 支付处理工具

工具用途支持的支付网关
Laravel Cashier订阅计费Stripe, Paddle
Srmklive/PayPalPayPal 集成PayPal REST API
Mollie/laravel-mollie欧洲支付支持多种欧洲支付方式
Omnipay/omnipay多网关支付支持多种支付网关

5.4 Laravel 12 前端框架

框架用途优势
Livewire交互式 UI无需编写 JavaScript
Inertia.js单页应用结合 Vue/React 和 Laravel
Vue 3前端框架响应式 UI 开发
React前端框架组件化开发
Tailwind CSS样式框架实用优先的 CSS 框架

5.5 Laravel 12 部署和监控工具

工具用途优势
Laravel Forge服务器管理一键部署 Laravel 应用
Laravel Vapor无服务器部署部署到 AWS Lambda
Docker容器化一致的部署环境
Kubernetes容器编排自动扩缩容
Prometheus监控系统多维指标监控
Grafana数据可视化直观的监控面板
Sentry错误跟踪实时错误监控

6. Laravel 12 多租户最佳实践

6.1 Laravel 12 多租户数据隔离

  • 使用中间件:通过中间件识别和验证租户
  • 数据库前缀:使用租户 ID 作为表前缀
  • 连接池管理:合理管理数据库连接
  • 缓存隔离:为不同租户使用不同的缓存键
  • 文件存储:为不同租户创建独立的存储目录

6.2 Laravel 12 多租户性能优化

  • 数据库索引:为租户相关字段添加索引
  • 查询优化:避免跨租户查询
  • 缓存策略:合理使用缓存减少数据库查询
  • 队列处理:将耗时操作放入队列
  • 水平扩展:根据租户数量和负载进行扩展

6.3 Laravel 12 多租户安全措施

  • 租户隔离:确保租户之间数据隔离
  • API 速率限制:防止 API 滥用
  • 输入验证:严格验证用户输入
  • HTTPS:启用 HTTPS 加密
  • 定期审计:定期检查安全漏洞
  • 数据备份:定期备份租户数据

6.4 Laravel 12 多租户可扩展性

  • 模块化设计:将功能拆分为独立模块
  • 插件系统:支持第三方插件
  • API 设计:提供完整的 API
  • 配置管理:集中管理租户配置
  • 功能开关:基于订阅计划启用/禁用功能

7. Laravel 12 SaaS 部署策略

7.1 Laravel 12 SaaS 云服务选择

云服务提供商优势适用场景
AWS全面的服务,全球覆盖大型 SaaS 应用
Azure企业级服务,集成微软产品企业级 SaaS 应用
Google Cloud强大的 AI 服务,全球覆盖需要 AI 功能的 SaaS 应用
DigitalOcean简单易用,性价比高中小型 SaaS 应用
Vultr全球数据中心,价格实惠全球用户的 SaaS 应用

7.2 Laravel 12 SaaS 容器化部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# docker-compose.yml
version: '3.8'

services:
app:
build: .
ports:
- "8000:80"
volumes:
- .:/var/www/html
environment:
- APP_ENV=production
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis

db:
image: mysql:8.0
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=saas_app
- MYSQL_USER=saas
- MYSQL_PASSWORD=password

redis:
image: redis:7.0-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data

horizon:
build: .
command: php artisan horizon
volumes:
- .:/var/www/html
environment:
- APP_ENV=production
depends_on:
- app
- redis

volumes:
mysql-data:
redis-data:

7.3 Laravel 12 SaaS 自动扩展

  • 水平扩展:根据负载自动增加实例数量
  • 垂直扩展:根据需要增加单个实例的资源
  • 数据库分片:根据租户 ID 进行数据库分片
  • 缓存集群:使用 Redis 集群提高缓存性能
  • CDN 加速:使用 CDN 加速静态资源

7.4 Laravel 12 SaaS 监控和告警

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# docker-compose.monitoring.yml
version: '3.8'

services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml

grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin

node_exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"

redis_exporter:
image: oliver006/redis_exporter:latest
ports:
- "9121:9121"
environment:
- REDIS_ADDR=redis:6379

volumes:
grafana-data:

8. Laravel 12 SaaS 实战案例:构建 TaskFlow 应用

8.1 Laravel 12 SaaS 项目背景

  • 项目名称:TaskFlow
  • 项目类型:任务管理 SaaS 应用
  • 目标用户:中小企业和团队
  • 核心功能:任务管理、项目管理、团队协作、时间跟踪、报表分析
  • 订阅计划:免费版、基础版、专业版、企业版

8.2 Laravel 12 SaaS 架构设计

1. 技术栈

  • 后端:Laravel 12、PHP 8.2
  • 前端:Vue 3、Tailwind CSS、Inertia.js
  • 数据库:MySQL 8.0
  • 缓存:Redis 7.0
  • 队列:Laravel Horizon、Redis
  • 认证:Laravel Sanctum
  • 支付:Laravel Cashier、Stripe
  • 部署:Docker、Kubernetes、AWS

2. 多租户实现

  • 架构模式:共享数据库,隔离架构
  • 表结构:使用租户 ID 作为外键
  • 数据隔离:通过中间件和查询作用域实现
  • 缓存策略:使用租户 ID 作为缓存键前缀

3. 目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
app/
├── Http/
│ ├── Controllers/
│ │ ├── Tenant/
│ │ │ ├── TaskController.php
│ │ │ ├── ProjectController.php
│ │ │ └── TeamController.php
│ │ └── Admin/
│ │ ├── TenantController.php
│ │ ├── PlanController.php
│ │ └── SubscriptionController.php
│ ├── Middleware/
│ │ ├── IdentifyTenant.php
│ │ └── TenantAccess.php
│ └── Resources/
├── Models/
│ ├── Tenant.php
│ ├── User.php
│ ├── Plan.php
│ ├── Subscription.php
│ ├── Task.php
│ └── Project.php
├── Providers/
│ ├── TenantServiceProvider.php
│ └── AppServiceProvider.php
└── Services/
├── TenantService.php
├── SubscriptionService.php
└── BillingService.php

8.3 Laravel 12 SaaS 核心功能实现

1. 任务管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// app/Models/Task.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
protected $fillable = [
'tenant_id',
'project_id',
'user_id',
'title',
'description',
'status',
'priority',
'due_date',
'completed_at',
];

protected static function booted()
{
static::addGlobalScope('tenant', function ($query) {
if (app()->has('tenant')) {
$query->where('tenant_id', app()->get('tenant')->id);
}
});
}

public function tenant()
{
return $this->belongsTo(Tenant::class);
}

public function project()
{
return $this->belongsTo(Project::class);
}

public function user()
{
return $this->belongsTo(User::class);
}
}

2. 订阅管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// app/Services/SubscriptionService.php
namespace App\Services;

use App\Models\Tenant;
use App\Models\Plan;
use App\Models\Subscription;
use Carbon\Carbon;

class SubscriptionService
{
public function createSubscription(Tenant $tenant, Plan $plan, $paymentMethod)
{
// 创建订阅记录
$subscription = Subscription::create([
'tenant_id' => $tenant->id,
'plan_id' => $plan->id,
'status' => 'active',
'starts_at' => Carbon::now(),
'ends_at' => Carbon::now()->addMonth(),
'payment_method' => $paymentMethod,
]);

// 更新租户状态
$tenant->update(['status' => 'active']);

return $subscription;
}

public function cancelSubscription(Subscription $subscription)
{
$subscription->update([
'status' => 'cancelled',
]);

return $subscription;
}

public function renewSubscription(Subscription $subscription)
{
$subscription->update([
'status' => 'active',
'ends_at' => Carbon::now()->addMonth(),
]);

return $subscription;
}
}

8.4 Laravel 12 SaaS 性能优化

  • 数据库索引:为租户 ID、用户 ID 等字段添加索引
  • 查询优化:使用 eager loading 减少 N+1 查询
  • 缓存策略:缓存热点数据,如租户配置和计划信息
  • 队列处理:将邮件发送、报表生成等耗时操作放入队列
  • 水平扩展:使用负载均衡和自动扩缩容

8.5 Laravel 12 SaaS 监控和告警

  • 应用监控:使用 Laravel Telescope 和 Sentry
  • 服务器监控:使用 Prometheus 和 Grafana
  • 数据库监控:监控查询性能和连接数
  • 队列监控:使用 Laravel Horizon
  • 告警机制:设置合理的告警阈值

9. 总结

Laravel 12 是构建 SaaS 应用的理想框架,其优雅的架构、强大的生态系统和丰富的功能使其成为开发者的首选。通过本文的介绍,开发者可以掌握:

  1. SaaS 架构设计:理解单租户和多租户架构的区别和选择
  2. 多租户实现:使用 Laravel 12 实现多租户架构
  3. 订阅管理:使用 Laravel Cashier 管理订阅和支付
  4. 常用框架和工具:掌握 SaaS 开发中常用的框架和工具
  5. 最佳实践:遵循 SaaS 开发的最佳实践
  6. 部署策略:选择合适的部署方案和云服务
  7. 性能优化:提高 SaaS 应用的性能和可靠性
  8. 安全措施:确保租户数据安全和隔离

在实际开发中,开发者应该根据项目的具体需求和规模,选择合适的架构模式和技术栈。同时,应该注重代码质量和测试,确保应用的可靠性和可维护性。

随着云计算和 SaaS 模式的不断发展,Laravel 12 也在不断演进,为开发者提供更多强大的功能和工具。通过持续学习和实践,开发者可以构建出高质量、高性能的 SaaS 应用,满足用户的需求,实现商业价值。