This year proved multiple new use cases for e-commerce. A huge amount of businesses go through rapid digitalization and are actively looking for new tools.
We've recently seen a growing need for technologies enabling e-commerce processes for niche industries, that can be enabled in a SaaS model. While Spree Commerce gives us ready-made e-commerce processes, another aspect to consider is how to scale that across multiple customers. That's where multi-tenant may come in handy.
There are two major scenarios for creating multi-tenant applications for e-commerce:
Muti-tenant however won't help run multivendor marketplaces, where we want to display products from multiple vendors together.
Fortunately, there's a great solution for multi-tenancy for Rails applications called Apartment. It provides base tools for creating isolated environments via separate databases(or schemas in Postgres) as well as for switching between tenants based on domain or request headers. It's also fairly customizable, so you can implement your strategies for selecting tenants.
For Spree-based stores, the easiest way to integrate with Apartment is via Spree Shared gem. It provides a basic wrapper for Apartment, that also takes into consideration Spree's built-in mechanisms for multi-store setups.
A common approach for creating isolated environments for each client is to run a separate application instance for each of them. This works well in small setups with a few clients, but once this number grows to hundreds, you'll run into multiple issues.
Running separate application instance for each client:
Running multiple tenants in a shared environment:
When running a separate instance of the same application for each client, you'll quickly run into a problem when rolling out a new update requires a lot of time. At some point, the codebase may also diverge (for example when an important client asks for a feature and it's "easier" to create a separate branch of code just for them. You may also run into a situation when someone didn't deploy the latest update to one of the clients for some reason.
Running a multi-tenant application naturally forces us to use a single codebase across all clients. In long run, this means lower maintenance and support costs.
When running separate instances of the same application for each client, adding more clients usually means adding more servers. In most cases, this means reserving more computing capacity than is required.
Multi-tenant setups use shared infrastructure while allowing to run a large number of virtual instances on a single server. The need for adding more instances comes from increasing traffic, not from an increasing amount of installations.
Even with a good amount of automation, provisioning new servers for new application instances take a lot of time. It also means maintaining an increasing amount of configurations, for each new environment.
In the simplest scenario, creating new tenants in a multi-tenant setup is a matter of creating a new database entry with the configuration for the tenant. This makes it easier to onboard new tenants and also makes it easier to automate the process.
Provisioning new application servers require creating separate DNS entries for each of them. With a multi-tenant setup, you can use a single wildcard DNS record that will be routed to the application server. The server will then pick the right tenant automatically based on e.g. the subdomain used in the request. For large installations, this is a simpler approach that doesn't require additional automation for managing DNS entries.
While there are ready-made solutions for supporting multi-tenancy in Spree-based applications, there are some aspects that you need to think about ahead as it will affect how you configure both the Apartment gem and the infrastructure.
As the amount of requests increases, scaling multi-tenant apps need to be considered looking at multiple angles:
Depending on SLAs, you may want to create a setup where some clients get dedicated infrastructure to ensure the best performance and reliability.
One of the choices you will face when designing a multi-tenant solution is how to structure databases to isolate tenants. There are two approaches:
Apartment gem supports both approaches however when configured for multiple database setup it may have performance issues as switching tenants, which may happen often, will require establishing a new database connection. Depending on network infrastructure, this may cause considerable performance degradation compared to switching databases within the same server. Another issue you will face is in multi-threaded servers, you may encounter race conditions as Rails’ connection pool is not thread-safe. Because of that, you will need to switch to a single-threaded mode.
While multi-tenant deployments aim at having a single infrastructure capable of running multiple isolated client environments, there are some situations where it won't be enough by itself.
The most common scenario is when due to data-processing laws (for example servers and data of EU -based clients should remain in the EU), you may still need to create separate "clusters" in separate infrastructures. Then, based on the requirements, you can have a master application routing clients to the right cluster based on tenant parameters.
A huge advantage of solutions like Apartment and Spree Shared is that adding new tenants can be done programatically. What you need to remember is that the preferred way of loading tenant configuration is to do that in the application's initializers. This will require you to restart the application after adding new tenants for them to become visible.
For a fully self-service model, where new tenants can be created for example via a web panel, you'll need to automate the process of restarting servers (in our experience, a proper configuration of k8s + Ingress can do a great job here).
Another aspect is that creating a new tenant can also require additional provisioning in external services. For example, if you're offering your tenants an option to have a recommendations engine provided by Google Recommendations AI, the process for creating a new tenant will also require you to provision relevant resources & service accounts in GCP. Because of that, creating a new tenant can easily turn into a multi-step long-running process that you need to plan for.
Applications running in multi-tenant setup usually have two classes of configuration:
When designing multi-tenant applications, you need to review configuration items and integrations and decide on which of them go to each bucket. Be especially careful with external services - sharing a single account in e.g. Twillio may be more cost-effective, but it may also make it difficult to do a cost-breakdown between tenants.
In our experience, multi-tenancy provided by Apartment gem in Spree is a great tool for building e-commerce applications that require creating isolated environments for clients.
While it's fairly simple to do a basic integration, there is a number of parameters that need to be taken into account while designing such solutions. Contact us if you'd like to know more!