Tenant class

The center of the multi-tenant architecture is the Spree::Tenant class. This is the highest level class in the multi-tenant system.

Every tenanted model includes a tenant_id column. This is used to identify which tenant a record belongs to. It’s row-level multi-tenancy.

After creating a new tenant it will automatically create store and run Spree seeds to populate data such as countries, zones, tax rates, etc.

Tenanted models

All models that are tenanted inherit from SpreeMultiTenant::Base class. spree_multi_tenant automatically sets the Spree.base_class to SpreeMultiTenant::Base so all standard Spree models will be tenanted (with an exclusion of Spree::CustomDomain).

When creating a new model that you want to be tenanted remember to inherit from SpreeMultiTenant::Base and not from Spree::Base and add tenant_id column to the table, eg.

bin/rails g model Spree::NewModel tenant:references name:string
class Spree::NewModel < SpreeMultiTenant::Base
end

You don’t need to add belongs_to :tenant to the model, it’s handled by gem.

Tenant selector

By default the current tenant is selected based on the subdomain or Spree::CustomDomain. The selector works in tandem with the default Spree::Stores::FindCurrent finder.

First it finds the current store, and later sets the current tenant based on the store’s tenant_id.

Tenant record is accessible in views and controllers as current_tenant. In models you can access it via SpreeMultiTenant.current_tenant.

One tenant can have multiple stores. Calling Spree::Store.default will return the default store for the first tenant.

Code isolation

All code run when SpreeMultiTenant.current_tenant is set will be executed in the context of the tenant, what that means:

  • all finder queries will always attach tenant_id = <current_tenant.id> to the query
  • all writes will automatically set tenant_id to the current tenant ID (you don’t need to do it manually)

insert_all / upsert_all methods will require you to manually set tenant_id to the desired value.

Background jobs

Background jobs enqueued when SpreeMultiTenant.current_tenant is set will be executed in the context of the tenant.

Enforce tenant context

Sometimes when you’re not sure if the tenant context is active you can use SpreeMultiTenant.with_tenant to enforce it, eg.

SpreeMultiTenant.with_tenant(tenant) do
  # all operations here will be executed in the context of the tenant
end

Excluding tenant context

Sometimes you want to run a piece of code outside of the tenant context, for that you can use SpreeMultiTenant.without_tenant

SpreeMultiTenant.without_tenant do
  # all operations here will be executed outside of the tenant context
end

Command line

When using bin/rails console or bin/rails runner you can set the tenant context by:

SpreeMultiTenant.current_tenant = Spree::Store.find_by(code: 'my_store').tenant