The importance of context
Brian Chavez asks about adding multi tenancy using contextual sessions:
A topic for a future post perhaps, it would be really interesting to see how you would architect a web application with a contextual session management that supports multi tenancy that is a scalable keeping in mind the possibility of web farms / load balancers.
I spoke about this in my DevTeach talks about advanced OR/M and IoC. Let us start by defining multi tenancy. It is the ability to serve several customers on the same installed application. What do I mean by that?
Wikipedia defines Multitenancy as:
Multitenancy refers to the architectural principle, where a single instance of the software runs on a software-as-a-service (SaaS) vendor's servers, serving multiple client organizations (tenants). Multitenancy is contrasted with a multi-instance architecture where separate software instances (or hardware systems) are set up for different client organizations. With a multitenant architecture, a software application is designed to virtually partition its data and configuration so that each client organization works with a customized virtual application instance.
Broadly, there are three major challenges for multi tenant applications:
- Ensuring separation between the data of each client.
- Allowing customization of the data for each client.
- Allowing customization of behavior for each client.
For the first point, I tend to use a separate database per client, this make it significantly easier to deal with a lot of tasks, chief among them is that you literally can't accidentally expose another client data and letting the client access his own data is trivial (especially important if client want to get backups of his data).
For the second and third, I use the idea of the context.
The context is just that, the current context of whatever work we are performing. As a simple example, the context will contain the current user, how to access the database, the relevant services, etc. The context is an important concept, although I tend to favor implicit context rather than the explicit one.
An explicit context is something like this:
Or perhaps using an IContext with the same approach. It explicitly wraps all the contextual parts of the application.
As I said, I prefer to have an implicit context, where I don't explicitly need to deal with the context, rather, each of the service and data items that are present in the context. Rather, I deal with the contextual issue by extending the container to understand how it should deal with the context. That is, resolving the IAuthenticationService is an operation that is based on the business logic related to the current context.
This allows me more freedom overall and ensure that I don't have such things as DoSomething(IContext context), which doesn't really give me any hint about what it is doing.
Now, how does the idea of context allow me to deal with multi tenancy?
Quite simply, actually. For data customization, I simply create a separate session factory per each client, they can make whatever changes they want, and I can create a derived model on top of the default one, to match their needs. The usage of the base model means that I can still work with all the default services, and let NHibernate take the brunt of dealing with the modified schema and data.
For behavior customization, I have two options. If the behavior is part of the model, than obvious it should go in the derived model. If it is part of the services that we built for the application, we can simple build a derived service with the requested behavior and add it as part of the specified customer context.
This approach hugely simplify the need to deal with complex requirements such as multi tenancy. The real secret, I must confess, is that from the point of view of the application, this multi tenancy is not really felt. It just happens.
Comments
I definitely agree that seperate database per client is best if you can deal with the management issues. Much easier to do that with a handful of clients than hundres, but it takes so much extra work out of the equation when the clients are really supposed to be isolated.
I didn't quite understand something, do you still have a Context object? Or do you use IoC.Resove<IAuthenticationService> and let the container take care of it?
I see a similar problem with configuration (app.config etc). We're having a debate on my current project. One opinion is that the config is a service that should be relied upon and used by the entire application, at any level. I don't like this, I'd rather that classes were configured during instantiation using the constructor, or by the IoC container, or by a factory (that might decide to use the app.config). That makes it more testable, more explicit, and flexible. How do you deal with this?
I don't have a context object, in most cases. The context is implicit, as I said.
You get the IAuthService in the ctor.
Agreed about the configuration, btw
I've solved this problem in a different way: one database, and most tables have an extra uniqueidentifier column that identifies the client. Then I map each client to their own virtual directory in IIS. A binsor config extends the embedded config, injecting some credentials into the ServiceModel (referencing that uid). The client must supply these same credentials when connecting.
Joao,
Yes, that works, but...
a) It requires special attention to ensure that you always qualify by the client id.
b) sharding is hard or impossible
c) you can't let the client have a backup of the client's data
Hi,
I agree with your design, some years ago I made some .net 1.1 projects with the same design approach, my simple and little question is what kind of "object discriminator" you use to create uniquely per-user object? I'll explain better, I think there's a place where you decide to create "contextual" objects, this deciding is made by what? maybe can you post an example? just to make what's the difference between mine and yours implementation
Ciao
Manuel,
I use several techniques, based on how this is supposed to work from the client perspective.
a) host name
b) common login and than by the login data
c) geo location
d) telepathy
Comment preview