How to handle multi-tenancy in Kentico 12 MVC
With the shift in development model towards MVC it is time to clarify and describe usage of certain features and setups that work seamlessly in the Portal Engine but are not as clearly defined with MVC. One such example is creating and managing a multi-tenancy setup.
In the old portal engine model, this was pretty straightforward. You would create a new site in your Kentico instance, set it up with right properties such as a domain name that matches domain bindings in IIS, and you would be done.
There are two possible approaches to deploying and managing multi-tenancy with the Portal Engine, but in both cases, you will still use a single code base, application, and database to handle and route all requests for pages and resources on your web sites. Kentico’s routing automatically identifies and routes requests to their respective sites and it makes everything work seamlessly.
The first approach is to have a single IIS site running with multiple sub-applications. These applications are just IIS applications that are directing requests to the same Kentico code base on disk. This single code base serves the site-specific data based on identification by domain (or subdomain).
The other approach is to have multiple separate domains bound to a single IIS site via site bindings. This ensures that the site is listening on multiple domains. In this setup, the IIS web site sends requests to Kentico, which serves responses accordingly. Both setups are shown in the diagram below:
In both cases, you must account for Kentico licensing as each domain or subdomain that serves different content requires its own license.
When it comes to MVC multitenant installation, you must account for another variable in this pipeline: The MVC application. The MVC application is the one that handles the request for the site served to the public instead of Kentico’s codebase. It connects and synchronizes with the Kentico administration application via a shared database and Kentico’s APIs.
For the purpose of this article I would like to clarify, or rather establish, the fact that a “site” in the Kentico administration application no longer equals a full web site with its own URL and presentation (in short Kentico Site != web site).
Instead, we can think of a Kentico site as a more complex data container rather than an actual site. This difference can influence where we will store data when designing the infrastructure for the whole application. For example, if we are planning to have an organization with multiple branches in one instance, we can have each branch manage their own “site”, and we can have one global “site” for shared content.
The MVC application comes into play for the public site. It is responsible for handling the business logic that decides which data to load and from which container (site) to load it from. It’s the MVC application in which we decide whether we want to pull data from “Site 1” or “Site 2” to the end user. Now you have greater freedom to create an application that will be tailored to the client’s needs. It comes at a cost though, since Kentico’s routing now no longer handles everything automatically.
Thus, the architecture of multitenant applications will be a bit different, but fortunately not by much. Usually, we want to keep everything to use just single codebase to keep deployment processes as simple as possible. When handling sub-sites in MVC, this is easy to handle since it is just one MVC application talking to administration backend and loading data from different sites based on routes configured and registered either through RouteConfig or route attributes:
When the sites are different enough, the decision to keep a single codebase depends on whether the sites share some content and functionality. If they do not, these would be better suited to be separate applications and sites in IIS. You just cannot be abstract enough to be able to cover 20 different web sites in a single application without creating an unmaintainable mess. This would also make deployment and upkeep really complicated.
However, you need to be aware that each MVC application connecting to the database acts as a separate web farm instance, meaning that the number of web farm servers can be quite large. You also must account for details like the number of web site visitors, a frequency of updates to content, and the amount of content as these factors will influence performance. You should also consider whether it makes sense to break the sites into multiple administration instances. When it comes to weighing these factors, the number of visitors is somewhat obvious: having more visitors means more server load, and even on a well-cached site, the traffic may need to be distributed across multiple servers. The same thing applies for dynamic (frequently updated) content since updating content generates web farm tasks. For example, if we are serving 200 constantly updated sites, it will create 200 x N (where N is the number of updates) web farm tasks, which can cause web farm tasks and related tables to be a bottleneck:
We’ll explore one more approach to a multi-tenant setup with one Kentico administration and a single MVC application with the same codebase handling all the different URLs. In this configuration, the application is responsible for processing requests, figuring out where the request is coming from (which domain), and serving the data accordingly.
Here is a diagram showing this configuration:
This approach is very similar to approaches outlined previously for Kentico’s Portal Engine. However, in that case, you could rely on Kentico’s rewriting engine to process requests, determine the domain, and handle URLs. Of course, due to how MVC routing works this is not possible with MVC applications. There is a lot more freedom as was already mentioned, but it requires developers to have knowledge of the application’s desired structure and requires them to handle routes and URLs dynamically and independently of the stored content. In the diagram above we have the concept of a domain controller that works as an extension of a standard controller. The domain controller detects the request context and evaluates the domain. There are other approaches to tackle this, such as custom route handlers or other middleware. I hope to go into more technical details on these and other approaches in future articles. The domain controller approach evaluates which controller and action will be loaded to load data from the correct site.
This approach also allows mixing of content across different sites, since we can have one site in Kentico’s administration serving as a container for shared content like brand images, texts, and other general content. Since our application code is not limited by explicitly by domain, controllers can choose to load data from anywhere.
One more thing to consider is which objects are site-level and which are global-level. This applies in both MVC and Portal Engine projects as this is Kentico data architecture and pattern. Kentico has objects which can be only global, only site or both site and global, and it is up to us to consider whether these will have any impact on the stored data. In code we can verify if the object supports global/site assignments, it can be figured by looking and object TYPEINFO property called SupportsGlobalObjects.
For example, users can be shared across sites which makes them easier to manage across all sites and the websites can have more unified authentication. Another nice example would be contacts which are global only and will be shared across all sites. Application architecture for multi-tenancy must take this into account.
Performance largely depends on the implementation of the MVC application, and how efficient it is at caching and handling data. Theoretically, with a single shared application instance for all sites, it can load resources more efficiently than multiple applications. In this case, the capacity of the web server becomes potentially more important than the performance of the database server.
In summary, multi-tenant web sites are not only possible with the Kentico MVC approach, but they provide some benefits over the Portal Engine approach. However, they also require more planning in the architecture phase and can be more demanding from project management perspective. This is because you need to consider the overall content structure and most efficient way of storing content before putting in a lot of effort on developing actual MVC application to avoid coding around poor decisions.