Het klinkt mij meer als een probleem op het gebied van het werken met een database dan een MVC specifiek probleem. Ik ga er even van uit dat je op dit moment ADO.NET entity framework gebruikt in combinatie met MVC. Gebruik je iets anders, dan werken de principes uit deze post nog steeds, maar dan zul je zelf waarschijnlijk wat meer met SQL queries moeten doen.
Er zijn verschillende technieken die je kan toepassen om multi-tenancy te realiseren in een applicatie.
Het verschil tussen de technieken zit hem in de volgende aspecten:
- De schade die je kan aanrichten, mocht het niet helemaal dicht zitten.
- De (on)mogelijkheid om data te restoren vanuit een backup, mocht dat nodig zijn.
- Onderhoudbaarheid van je database/applicatie
Hoe dan ook krijg je te maken met meer of minder ellende op deze punten. Er zijn echter altijd wel oplossingen te vinden om het leed te verzachten. Zeker waar het onderhoudbaarheid betreft. Een goed stuk tooling om bijvoorbeeld databases uit te rollen voor een klant kan al heel veel schelen.
Ik denk eerlijk gezegd dat je in eerste instantie vooral moet gaan kijken naar de beveiliging en op het laatst pas naar onderhoud. Het wel of niet hebben van losse databases kan belangrijk zijn, voor bijvoorbeeld het kunnen overdragen van data of het repareren van een probleem (door middel van een restore van de backup of het uitrollen van een patch op de database(s)).
Mogelijkheid 1: Data scheiden, door een tenant kolom op te nemen in je tabellen.
Bij deze techniek, maak je in de database een tabel aan, waarin je de tenants (klanten) neerzet. Elk account dat wordt aangemaakt, wordt vervolgens aan een klant gekoppeld. Als je vervolgens andere data in de database gaat inserten, zul je ervoor moeten zorgen dat telkens het ID van de tenant van die gebruiker, bij in de tabel wordt gezet.
Met entity framework, kun je dat doen door bijvoorbeeld de property TenantID op een object, te vullen. Dit kan bijvoorbeeld door de SaveChanges methode op de DataContext te overriden. Maar je kan het ook met iets meer handwerk, prima in je controllers doen. Dat laatste zorgt wel dat je overal verspreid over de applicatie, dergelijke code hebt staan.
Bij het ophalen van data zul je vervolgens telkens de volgende Where() methode call moeten toevoegen aan de queries die je stelt op de database. Bijvoorbeeld:
C#:
1
| DataContext.Tasks.Where(task => task.TenantId == CurrentTenantId) |
Voordelen:
- Slechts een database, dus bij een nieuwe klant hoe je alleen maar een extra Tenant toe te voegen.
Nadelen:
- Als het niet dicht zit kunnen bezoekers van je site overal bij. Goed testen dus!
- Mogelijk meer onderhoud, omdat je oval in de code extra Where statements moet toevoegen.
- Restore van backup betekend restore van alle data voor alle klanten
Let op! Bij deze techniek kun je wel werken met een aparte data file per data partitie. Je kan op een tabel aangeven dat alle dat die voldoet een bepaalde conditie in een specifieke file terecht moet komen. Op die manier kun je ervoor zorgen dat data van een specifieke tenant altijd bij elkaar in dezelfde file staat. Als er dan iets misgaat voor die tenant, restore je alleen die data file van de database. Er is een catch: dit werkt overigens alleen voor SQL server 2008 en hoger.
Mogelijkheid 2: Gescheiden schema's
Een tweede mogelijkheid is om te werken met schemas in de SQL server database. Hierbij maak je per tenant een nieuw schema aan, waarin je vervolgens alle tabellen, views en stored procedures nogmaals aanmaakt. Om te zorgen dat gebruikers op "hun" schema uitkomen, moet je zorgen dat voor het account waarmee hij naar de database gaat, het default schema is ingesteld op het schema dat voor hem bedoeld is.
Je hebt bij deze techniek dus geen extra code nodig in je applicatie om de data te scheiden. Maar je moet nu wel ervoor zorgen dat gebruikers met hun eigen account de database in gaan. Hiervoor kun je gebruik maken van
Impersonation in combinatie met een windows account per user. Er zijn andere mogelijkheden, zoals SQL text login per user en dan dynamisch de connectionstring instellen, maar dat is qua onderhoudbaarheid dan weer niet zo handig. Want als je een Windows account hebt per user, kun je dat account ook rechten geven op bijvoorbeeld zijn eigen uploads map. Daarmee voorkom je dan ook direct dat gebruikers bij elkaars uploads kunnen.
Voordelen:
- Het is moeilijker/onmogelijk om bij elkaars data te komen, heb je het in de applicatie niet helemaal dicht zitten, dan nog kunnen gebruikers niet bij de spullen van een ander komen.
- Omdat je gebruik maakt van een Windows account voor je gebruikers, kun je die ook rechten geven op upload folders op je webserver en daarmee dat gedeelte ook afschermen. Hiervoor moet je natuurlijk wel nog een stukje logica bakken, om op de juiste map uit te komen.
- Nog steeds maar één database op je server.
Nadelen:
- Restore van backup, betekend restore van alle data voor alle klanten
- Uitrol voor een nieuwe klant betekent: Nieuw schema in de database, uitvoeren scripts, nieuwe logins maken, koppelen aan het juiste schema. Foutgevoelig als je dit met de hand moet gaan doen.
Mogelijkheid 3: Gescheiden databases
Bij deze techniek maak je per tenant een database aan, met daarin alle tabellen, views en stored procedures voor die klant. Daarnaast heb je dan nog een zogenaamde configuratie database nodig om bij te houden, welke klant nu precies welke database moet gebruiken. Tot slot heb je dan ook nog iets nodig bij de profielen van de gebruikers, om ervoor te zorgen dat je van een gebruiker kan bepalen welke klant hij bij hoort.
Alles staat en valt bij deze techniek, met het goed instellen van de connection string voor de
DataContext/DbContext. Verbind je met de goede database, dan gaat alles in een keer goed werken in je website.
Voordelen:
- Losse databases, restore van backup kan per klant worden uitgevoerd.
- Klanten kunnen niet bij elkaars data komen, het staat simpelweg niet in de database van de andere klant.
- Overdracht van gegevens aan de klant is eenvoudig te regelen, je geeft hem de backup van zijn database.
Nadelen:
- Per klant moet je een nieuwe database uitrollen, configuratie database aanpassen en accounts koppelen.
- Als je veel klanten hebt, met elk hun eigen database krijg je veel databases die je moet backuppen en patchen. Als je dit wilt, investeer dan veel tijd/geld in het goed automatiseren van dit soort taken.