You can support multitenancy in your application by providing separate data partitions for multiple client organizations, known as tenants. This allows you to customize data values for each tenant, while keeping the same data schema for all tenants. This makes provisioning new tenants more efficient because you don't have to change data structure when you add a tenant.
Benefits of multitenancy
Firestore in Datastore mode allows a multitenant application to use separated silos of data for each tenant while still using:
- a single project
- a single logical structure for the kinds
- a single set of index definitions, because the kinds are the same logically for each tenant
Datastore mode enables multitenancy by providing namespaces.
Multitenancy and partitioned data
Datastore mode uses partitions to silo data for each tenant. The combination of a project ID and a namespace ID forms a partition ID, which identifies each partition. An entity belongs to a single partition, and queries are scoped to a single partition.
Specifying a namespace for an entity
You specify the namespace when you create the entity: after you create the entity, you cannot change the namespace. If you don't explicitly specify a namespace for an entity, it is automatically assigned to the default namespace, which has no string identifier.
Using namespaces with parent entities
An entity and all of its ancestors belong to one and only one namespace. This means that when you create an entity with another entity designated as parent, the child entity is in the same namespace as its parent: you cannot specify some other namespace.
Sample use case
A key benefit of multitenancy is having the same application serve multiple
client organizations. To achieve this benefit, for a given kind, your
application should behave the same regardless of the namespace. For example,
from the application’s perspective, an entity of kind Task
in one namespace
should logically be the same as an entity of kind Task
in all other
namespaces. Your application could then use a single set of index definitions
to support Task
queries, regardless of which namespaces contain Task
entities.
For example, consider a Task List application that silos data on a per user basis. The application could define namespaces based on user name, resulting in the following partitions:
Partition ID: project:"my_project_id"/namespace:"Joe" Partition ID: project:"my_project_id"/namespace:"Alice" Partition ID: project:"my_project_id"/namespace:"Charlie"
The application could define a logical structure of a Task
kind as follows, to
use for all namespaces:
kind: Task properties: - "done", Boolean - "created", DateTime - "description", String, excluded from index
When a user creates an entity of kind Task
, the entity is stored in the user’s
own partition, resulting in siloed data. The application processes Task
entities consistently across namespaces because only one schema is used for the
Task
kind. An application with siloed data and consistent behaviour would be
multitenant.
If the logical structure of a Task
kind differs by namespace, the application
would not be multitenant because it processes Task
entities differently
across namespaces. For example, consider Task
kinds that have different schema
based on namespace:
Task
entities in the Joe namespace exclude thedescription
property from the indexTask
entities in the Alice namespace include thedescription
property from the index
The application could query on the description
property for Alice’s Task
entities, but it could not query on the description
property for Joe’s Task
entities, so the application would not be multitenant.
Viewing namespaces in the console
To see statistics for the namespaces used in your project, visit the Datastore Dashboard page in the Google Cloud console. To programmatically determine which namespaces are used in your project, see Namespace queries.
If you need to group data within a tenant, you can categorize your data by kinds, and you can also organize highly related data with entity groups.