Skip to content
Goxiaoy edited this page Nov 28, 2020 · 3 revisions

There are two problems that you would face when you start to build a saas(multi-tenancy) system

    1. How to determine current scope of tenant
    1. How to do data isolation in the data storage

Terminology

  • Host: super admin site, where you can manage all tenants
  • Tenant: tenant site, where customers can use your software

For the quetion 1

go-saas uses the go context api to store current tenant value, and define a TenantResolver interface to resolve current tenant id or name

The DefaultTenantResolver which implements TenantResolver uses slice of TenantResolveContributor and call each Resolve function one by one. It uses the Chain-of-responsibility pattern

For most web applications, you can use the implemented TenantResolveContributor like

  • CookieTenantResolveContributor: resolve from a cookie value
  • DomainTenantResolveContributor: resolve from a domain regex
  • FormTenantResolveContributor: resolve from a from value
  • HeaderTenantResolveContributor: resolve from a header value
  • QueryTenantResolveContributor: resolve from a query value

For web application build on top of gin,

you can use the MultiTenancy middileware to inject resolved tenant value in every request, sample codes

For the quetion 2

After current tenant is resolved, you can call FromCurrentTenant(ctx context.Context) BasicTenantInfo to get current tenant

TenantStore: define a query interface to resolve tenant info including with connection string key-values

go-saas defines a ConnStrResolver to resolve connection string of current tenant

The DefaultConnStrResolver resolve connection string value from config directly. And the MultiTenancyConnStrResolver use TenantStore to query current tenant info, once a host environment detected, a fallback resolve behavior to DefaultConnStrResolver would be performed

For systems that use Multi-tenancy or Hybrid database mode

you can use NewEnableMultiTenancyDataFilter(ctx context.Context)context.Context to get a disable tenant filter context, and use this conext to access database

Default auto set tenant id value behavior

when you create an entity, if current tenant id is not equal with TenantId value of this entity, in tenant site, the value will be always normalize to current tenant id You can also disable with NewDisableAutoSetTenantId(ctx context.Context)context.Context to get a disable auto set context

For application use gorm

    1. Add HasTenant field to your entity or add MultiTenancy to your struct
type TestEntity struct {
	ID string
	MultiTenancy
}

type TestEntity1 struct {
	ID string
	TenantId HasTenant `gorm:"index"`
}
    1. Initialize a DefaultDbProvider, initialze a GormTenantStore, and initialize a GormTenantRepo you can see the sqlite sample in sample code
    1. Do use the same DefaultDbProvider to resolve *gorm.Db by context, the data isolution is automaticly done by gorm
    1. Do not forget to close all dbs

Future plan

  • Add support for more web frameworks
  • Add support for more orms