Fork me on GitHub
#datomic
<
2022-07-18
>
onetom05:07:46

Are there any recommendations somewhere about modelling multi-tenant data? Should we have a single tenant reference attribute, which is used on every kind of entity or should we have a ref attribute for every "entity-type"? eg:

{:tenant/ref [:tenant/uniq-id 123] :user/id 345}
{:tenant/ref [:tenant/uniq-id 123] :frob/id 678}
vs
{:user/tenant [:tenant/uniq-id 123] :user/id 345}
{:frob/tenant [:tenant/uniq-id 123] :frob/id 678}

onetom05:07:28

the 1st solution would allow using a single, common and also simple datalog :where clause or rule, to limit queries to a specific tenant, so it feels less error-prone. the 2nd solution allows operating on the whole collection of entity-types for a specific tenant, by simply using pull expressions, with a reverse lookup ref, eg. (d/pull db-val-for-all-tenants [:frob/_tenant ['*]] [:tenant/uniq-id 123]), which would return all frobs of tenant 123.

onetom05:07:36

if i have a single :tenant/ref, i would always need to write datalog queries, for operations, which would be effectively just simple pulls, then i would also need to unwrap them from the resulting, single element vectors, since there is no find-scalar option for Datomic Cloud (https://docs.datomic.com/cloud/query/query-data-reference.html#arg-grammar):

find-spec         = ':find' find-rel
find-rel          = find-elem+
find-elem         = (variable | pull-expr | aggregate)
like for Datomic On-Prem (https://docs.datomic.com/on-prem/query/query.html#query):
find-spec                  = ':find' (find-rel | find-coll | find-tuple | find-scalar)
find-scalar                = find-elem '.'
find-elem                  = (variable | pull-expr | aggregate)

onetom06:07:03

This talks mentions how filtered databases on Datomic On-Prem, combined with recursive datalog rules can safeguard per-tenant information retrieval: https://youtu.be/7lm3K8zVOdY?t=919 we would like achieve the same with Datomic Cloud, but it's unclear how.

onetom10:07:38

forgot to mention that in the above examples, that those :user/id and :frob/id are IDs of 3rd-party systems, so I also had to use a composite key, with a unique constraint, like this:

{:user/id        345
 :user/tenant+id ["tenant-123" 345]
 :tenant/ref     {:db/id          "tenant-123"
                  :tenant/uniq-id 123}}
to keep 3rd-party data from multiple tenants within the same DB.