Saturday 12 May 2018

ASP.NET MVC Multitenancy, Part 0 - Subdomain VS URL Path

With multitenancy you will need to decide how you are going to identify your tenants. There are two user friendly approaches, subdomain fictionaltenant.multitenantedapp.com or URL Path multitenantedapp.com/fictionaltenant/. In this post we are going to explore disadvantages and advantages of these two approaches.

Subdomain


Configuration

It is surprisingly easy to set this up for a simple deployment model. This is what you have to do:

Setup DNS record

I went to my domain registrar site and configured all my traffic on *.multitenantedapp.com to point to my Azure Web App domain e.g. *.multitenantedapp.com -> zkwildcardtest.azurewebsites.net.


Setup wildcard hostname

After that all I had to do was configure my Azure Web App to accept traffic for the hostname *.multitenantedapp.com. If you are using a web server such as IIS you will just need to change the IIS hostname binding.


Deploy your app

Just to make sure everything works as it should, I have deployed a simple test app that reads the URI and extracts subdomain from it. I went to tenanta.multitenantedapp.com and this is what I saw:


Why should you use this approach?


Advance estate management

For me advance estate management is the main reason why you would go for this approach. As your application scales you will want to split up your deployment zones. For example, you might want to deploy 50 tenants to zone A and other 50 tenants to zone b. What if some tenants in zone A are being noise neighbours (using your application a lot)? How will you migrate them to the zone C? With subdomain you can use DNS to redirect traffic, subdomain will remain the same, just the DNS mapping will need to change.

Customisation

You can enable customisation, so for example if you own fictionalcompany.com domain you can point files.fictionalcompany.com to fictionalcompany.multitenantedapp.com. This sounds simple but there is more to it. You need register this hostname on your web server. Also if you would like your connection to be secure you will need to get fictionalcompany to give you the subdomain SSL certificate.

Log quality

Here is how the subdomain IIS log looks like:

date: 2018-01-30 
time: 22:25:30
s-sitename: ZKWILDCARDTEST
cs-method: GET
cs-uri-stem: /
cs-uri-query: -
s-port: 80
cs-username: -
c-ip: 102.152.31.182
cs(User-Agent): -
cs(Cookie): -
cs(Referer): -
cs-host: fictionalcompany.multitenantedapp.com TENANT'S NAME APPEARS HERE
sc-status: 200
sc-substatus: 0
sc-win32-status: 0
sc-bytes: 1958
cs-bytes: 1066
time-taken: 0

cs-host (client-server host) actually shows fully qualified subdomain. This is great, as it is very easy to parse and hard to spoof. This can be used for business and security analytics.

URL Path


Configuration

There is not much to URL path setup. You just need to configure your web application's framework mapping. If you are using ASP.NET MVC it would look something like this:

  
namespace Web
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            //...

            routes.MapRoute(
                "multi",
                "{tenant}/{controller}/{action}/{id}",
                new {controller = "Dashboard", action = "Index", id = UrlParameter.Optional}
            ).DataTokens.Add("name", "webclient_multitenancy");

        }
    }
}

For full mapping explanation take a look at this article.

With this approach you can introduce proxy layer, that proxy layer can then direct your traffic to different deployment zones. So in a way you can achieve same thing. However you are introducing additional infrastructure, which will require implementation, deployment, hosting and maintenance. If you can avoid it, you should.

Why should you use this approach?

The main benefit is that it is simple to implement and requires no DNS work of any kind. However with this approach you do compromise on advance estate management, customisation and log quality.

Here is how the IIS log would look like with URL path:

   
date: 2018-01-30 
time: 22:25:30
s-sitename: ZKWILDCARDTEST
cs-method: GET
cs-uri-stem: /fictionalcompany/somepage/ TENANT'S NAME APPEARS HERE
cs-uri-query: -
s-port: 80
cs-username: -
c-ip: 102.152.31.182
cs(User-Agent): -
cs(Cookie): -
cs(Referer): -
cs-host: multitenantedapp.com THIS IS NOT SHOWING TENANT'S NAME ANYMORE
sc-status: 200
sc-substatus: 0
sc-win32-status: 0
sc-bytes: 1958
cs-bytes: 1066
time-taken: 0

The main thing to notice is that client server host is now not showing the tenant's name. Now you need to parse cs-uri-stem which is more difficult. Also if your application is a single page application that performs all operations via API, chances are that tenant's name will be in the header which means you will not know which tenant is making the API requests.

Conclusion

If you are starting to work on a large-scale project, I strongly recommend that you give it a bit extra time and invest in the subdomain implementation. It's just so versatile, it will help you scale long term. However if you are working for a small start-up, you don't have the DNS skills in your team and you are not sure where project is going you can just use the URL path to get you off the ground.


*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment.

No comments:

Post a Comment