The code shown in this post is in flight and I’m just writing this post to try to get more feedback and suggestions on the approach we’re going so far before doing anything silly like making an official release.
The Marten community has been working toward a 2.0 release some time in the next couple months (hopefully in June for my own peace of mind). Since it is a full point release, we can entertain breaking API changes and major restructuring of the code. The big ticket items have been improving performance, reducing memory usage inside of Marten, a yet-to-be-completely-defined overhaul of the event store. The biggest change by far in terms of development time is the introduction of multi-tenancy support within Marten.
From Wikipedia:
The term “software multitenancy” refers to a software architecture in which a single instance of software runs on a server and serves multiple tenants. A tenant is a group of users who share a common access with specific privileges to the software instance.
The gist of multi-tenancy is that you are able to store and retrieve data tied to a tenant (client/customer/etc.), preferably in a way that prevents one tenant’s users from seeing or editing data from other tenants — and yes, I have indeed seen systems that screwed up on this in harmful ways.
To make this a little more concrete, here’s a sample:
[Fact]
public void use_multiple_tenants()
{
// Set up a basic DocumentStore with multi-tenancy
// via a tenant_id column
var store = DocumentStore.For(_ =>
{
// This sets up the DocumentStore to be multi-tenanted
// by a tenantid column
_.Connection(ConnectionSource.ConnectionString)
.MultiTenanted();
});
// Write some User documents to tenant "tenant1"
using (var session = store.OpenSession("tenant1"))
{
session.Store(new User{UserName = "Bill"});
session.Store(new User{UserName = "Lindsey"});
session.SaveChanges();
}
// Write some User documents to tenant "tenant2"
using (var session = store.OpenSession("tenant2"))
{
session.Store(new User { UserName = "Jill" });
session.Store(new User { UserName = "Frank" });
session.SaveChanges();
}
// When you query for data from the "tenant1" tenant,
// you only get data for that tenant
using (var query = store.QuerySession("tenant1"))
{
query.Query<User>()
.Select(x => x.UserName)
.ToList()
.ShouldHaveTheSameElementsAs("Bill", "Lindsey");
}
using (var query = store.QuerySession("tenant2"))
{
query.Query<User>()
.Select(x => x.UserName)
.ToList()
.ShouldHaveTheSameElementsAs("Jill", "Frank");
}
}
There are three basic possibilities for multi-tenancy that we are considering or building:
- Separate database per tenant — For maximum separation of different client’s data, you can opt to store the information in separate databases with the same schema structure, with the obvious downside being more complicated deployments and quite possibly requiring more hosting infrastructure. At runtime, when you tell Marten what the tenant is, and behind the scenes it will look up the database connection information for that tenant and possibly create a missing tenant database on the fly in development modes. We don’t quite have this scenario supported yet, but we’ve done a lot of preparatory work in Marten’s internals to enable this mechanism to work without having to blow up application memory by duplicating objects underneath the DocumentStore objects for each tenant.
- Separate schema per tenant — Using a separate schema in the same database for each tenant might be a great compromise between data separation and server utilization. Unfortunately, some Marten internals are making this one harder than it should be. Today, you can opt to stick different document types into different schemas. My theory is that if we could eliminate that feature, we could drastically simplify this scenario.
- Multi-tenancy in a single table with a tenant id — The third possibility is to store all tenant data in the same tables, but use a new “tenant_id” column to distinguish between tenants. Marten needs to be smart enough to quietly filter all queries based on the current tenant and to always write documents to the current tenant id. Likewise, Marten has been changed so that you cannot modify data from any other tenant than the current tenant for a session. Most of the work to support this option is already done and I expect this to be the most commonly used approach.
Right now, we’re very close to fully supporting #3, and not too far away from #1 either. I have a theory that we could support a kind of hybrid of #1 and either #2 or #3 that could be the basis for sharding Marten databases.
We *could* also do multi-tenancy by having separate tables per tenant in the same schema, but that’s way more work inside of Marten internals and I just flat out don’t want to do that.
So, um, what do you think? What would you use or change?



If you’re curious, Storyteller pulls this off by using an 

