Laravel 实现多租户的一些思考
前言
前段时间一直在研究 Laravel 多租户相关的开发,写篇文章记录下一些实现思路和关键代码。
需求起源
创业的项目需要给每个合作的学校提供教学系统等服务,而且需要做到每个学校的前端访问入口不一样。为了方便管理,最开始的计划是给每个学校的前端单独部署,分配独立的访问域名,所有前端均访问统一的后台。
这个时候,问题来了:每个学校的应用数据是相对独立的,怎么实现从数据层面分隔学校,以及通过前端请求映射到对应学校的数据,才能更「优雅」?
解决方案
如果你用谷歌搜索「Laravel multi tenacy」,可靠的方案并不多,其中有一个 tenancy/multi-tenant 乍一看还不错。仔细研究了下文档,发现教程「又臭又长」,而且对 Laravel 是有点侵入式开发的意味,所以只好放弃。
分库隔离
分库隔离是一开始我的首选,每个学校的数据库均独立。为了区分学校,需要在每个学校的前端系统请求后端 API 的时候,带上一个「学校参数」。
开发 Laravel 中间件,截取该参数识别出学校,然后配置对应的数据库。凡是带上有效「学校参数」的请求,所有增删改查都将局限在学校独立的数据库中。
做得更彻底一点,可以每个学校的前后端都单独部署,同时内部后台要可以连接每个学校的数据库进行数据汇总,以便做一些数据报表相关的工作。
这个方案其实是我最心水的,以我现在使用的架构来看,多个学校应用的单独部署/升级等管理都比较轻松。然而,由于现有系统的种种局限,比如后台拆分、权限拆分,以及其它若干功能不好界定归属于学校还是管理后台,这个方案还是被我暂时枪毙了。
字段隔离
既然前一个方案被 PASS 了,那也只剩下最简单粗暴的「字段隔离」方法了,在每一个学校有关的资源加上「school_id」区分。
考虑到,学校系统的资源以及功能要远多于管理端的,如果学校资源的增删改查都要加上「school_id」的处理,怕不是要疯,又谈何优雅?
当然,这也是有解决办法的。首先说一下整体方案:
- 每个学校的前端单独部署,配置不同的访问域名和「学校参数」,API 请求统一的后端并在请求头部带上「学校参数」
- 后端需要给 Laravel 开发一个中间件,能够提取「学校参数」,并将识别到的学校信息写入到 SESSION 中
- 利用 Laravel Eloquent Model 的特性,写一个
InSchool
Trait,所有引入该 Trait 的 Model 在增删改查时,会自动处理「学校参数」,Trait 的实现方式可以参考 SoftDeletes
下面给一下 Trait 的参考实现,算是备忘,也给有需要的同胞一点思路:
1 | use Illuminate\Database\Eloquent\Builder; |
小结
有一些无关紧要的感慨:做开发,应该尽量做一些「一劳永逸」的设计,而不是纯粹地堆砌业务代码。
做开发或者设计,不敢追求面面俱到(或者说完美无缺),否则会陷入一个细节无法自拔,进度不前。但也不能在能预见到风险和大可能性的改动时,还只顾着赶进度,否则不是把自己坑了就是把后人坑了。
最后,还有一点很重要:写代码最重要的是看得开!