Previous entries in the series
In the last entry, we covered the basics of what I consider multi-tenancy to be, and why we might perhaps want to write our ASP.NET MVC web application with multi-tenancy in mind.
The "ASP.NET MVC" component (or front-end) of your multi-tenant application probably only covers a small fraction of your entire codebase but is also the first and often only contact your customer has with your application, so ends up being their first point of call when asking for changes to your system.
It also ends up being the most awkward part of to change because that's the nature of using a framework like ASP.NET MVC which is designed primarily to be used in single-tenant scenarios.
Before getting into the technical details of how I implement a multi-tenant app in this environment, it's worth covering the components of our chosen framework and establishing where to start.
Disclaimer: This will not be an overly technical post, and I apologise to those that want me to just jump right in and start talking code. This will be the last introductory post in the series - promise :)
Getting to the point, this is how I personally split up the MVC application concepts into themes and modules.
| Themes | Modules |
| CSS | Views |
| Theme-specific images | Partial Views |
| Theme-specific JavaScript | Controller actions |
| Master pages | Module-specific JavaScript |
JavaScript
Whether we like it or not, designers like to deploy JavaScript alongside their designs these days, most notably with libraries such as Niceforms and its associated brethren.
It therefore pays to make the distinction between functional and theme-specific JavaScript and allow both modules and themes to provide their own collections of scripts.
For shared libraries like jQuery etc, they can be made available as part of the core application, and modules and themes can take it for granted that it will be available for their use.
CSS
Most theming can be achieved by switching style sheets if the mark-up has been designed properly and this is an obvious candidate for theming support. Switching between style sheets is a trivial and well documented operation and can easily be achieved through the use of a HtmlHelper extension method. I'll assume I don't need to write an entry on achieving the switching between either the CSS or JavaScript, although as with anything if prompted I'll cover the subject. Master Pages Sometimes the client wishes for major structural changes to the web application, and CSS changes may not be enough. For this we have master pages although because of the increased cost of having to maintain the extra mark-up they should probably only be used as a last resort. I wrote a blog entry about the various methods of switching between the master pages at runtime a while ago so I won't be covering that again. For my purposes over the coming posts, I assume that the structure of the master page IS a part of theming, and that we are using sub-master pages and separate child master pages across the site for different interfaces. I therefore use the OnPreInit method mentioned in the above entry for the greatest amount of flexibility. Views + Partial Views New modules will require either new views or the ability to override existing views, and thus I consider views to be a functional aspect of the application. I have seen views and partial views used for theming in frameworks or products where actual functional module support was highly limited. I assume a multi-tenant system *does* have decent functional module support and thus they are part of modules and not theming. Mark-up in the views should simply be kept as theme-agnostic as possible, as themes won't be able to change it. I'll be covering this in this series, utilising the power of a custom view engine to find and replace views based on the currently active modules. Controller Actions These are obviously an important part of adding functionality to the application through the use of modules. Because we have the ability to not only add new views, but to modify them - we also need the ability to add new actions and indeed replace existing actions (as modified views may accept modified view models!) This is probably the hardest problem to solve and there are a few ways of solving it, this too will be coming in this series - using custom controller factories to compose or locate controllers dynamically based on the currently active modules. In Summary We have covered the components of an MVC web application and established where the boundaries lie between modules and theming support. In the next entry I'll be covering how to utilise view engines to achieve the per-module views and partial views.
2020 © Rob Ashton. ALL Rights Reserved.