Laravel Queues in Action (2nd edition) is now available!

Multi-tenancy in Laravel

Updated: May 17, 2020 — 2 min Read#multi-tenancy

I recently started a deep dive into multi-tenancy in Laravel, specifically the database-per-tenant approach, and created a video on how to achieve multi-tenancy without any packages. 

The video covers switching DB connections at runtime, writing migrations, seeding, and testing. You can watch it here:

Session Hijacking 

After the video was posted, one of the concerns that people shared was related to preventing session data that belongs to one tenant from leaking to another.

 Basically, a user in tenant A can modify the session cookie and log himself into a user on tenant B if that user has the same user ID. Horrifying!

 I decided to address this issue by making a video that explains the problem, why it happens, and how to deal with it.

 Changing Configuration At Runtime

 When you update your application's configuration at runtime you need consider two things:

  1. The updates happen before the component is used.
  2. Any cached instance of the component is flushed after the updates.

To explain this in detail, I made a video where I show you how to handle the cache component and make sure each tenant has its separate cache store by applying a prefix.

 Using the same approach you can update the configuration of any component in your application while switching between tenants. For example; you can change the mail "from" address to match the tenant's, you can configure a different slack channel for each tenant's error notifications, and so on.

Single Database vs. Database Per Tenant

 The good Laravel community on Twitter started a very interesting discussion on the single DB vs. per-tenant DB approaches.

The db-per-tenant approach guarantees customer isolation, it also makes it easier to move the dataset to different locations since it can be easily extracted. However, it requires more dev-ops work to configure replication, backups, etc... 

The single database approach requires no extra dev-ops work. However, to achieve data isolation you need to focus while writing every single query to make sure it's correctly scoped. In addition to this, the indices will grow large in size and a tenant with a large dataset will affect other tenants with smaller datasets.

It's a tough call indeed. If it's a business requirement to have separate databases then the decision is made for you already. But if you're choosing between the two approaches, I recommend that you go for the single database approach and scope your queries.

The multi-database approach is sexy I know, it means you'll just do Order::find() without having to use whereTenantId(). However, if you're handling dev-ops yourself, having to deal with hundreds of databases is not sweet I promise.

Let me know what you think. Also here's a link to part of the discussion: 

Multi-tenancy Packages & Resources

If you want to use a package instead of building your own way to multi-tenancy, here's a list of some noteworthy packages:

There's also a talk by Tom Schlick at Laracon US 2017 that I highly recommend. You can check it out here: 

Another interesting package to check is the Laravel Multi-domain Package. It allows a single Laravel Installation to work with multiple domains. This can be used to host multiple tenants on your application and configure an environment file for each tenant.

Do I need all this? 

No! You don't need to change configuration files or jump between databases or use any packages if you're building a multi-tenant application. You can build a multi-tenant application by just making sure your code is scoped for the tenant, the same way you scope your code for the logged in user.

Multi-tenancy seems confusing mainly because we want to write our code without worrying about the current tenant and have our application magically figure everything out for us. While this sounds cool, but if you want magic I highly recommend that you spend some time to understand how it works.

The downside of magic is it makes debugging hard. It also covers 99% of the use cases, once you hit an edge case that isn't covered you'll find that magic will fail you.

Whatever approach to multi-tenancy you choose, make sure you cover your application with enough tests so you can sleep well at night.

Hey! 👋 If you find this content useful, consider sponsoring me on GitHub.

You can also follow me on Twitter, I regularly post about all things Laravel including my latest video tutorials and blog posts.

By Mohamed Said

Hello! I'm a former Laravel core team member & VP of Engineering at Foodics. In this publication, I share everything I know about Laravel's core, packages, and tools.

You can find me on Twitter and Github.

This site was built using Wink. Follow the RSS Feed.