Steven
Steven

Reputation: 879

Implementing Domain Driven Design: Why include TenantId in all repository queries?

I’m trying to understand why Vaughn Vernon (in the Github Sample code for the red book) includes tenantId in every repository get or find method. Particularly those that are doing a basic getById.

In one example he uses username to provide the identity of a User, and the business rule that usernames must be unique within a tenancy, so:

class UserRepository:
    public User userWithUsername(TenantId aTenantId, String aUsername)

Makes perfect sense.

But in his other BCs he is using an identity value object based on a GUID, but still combines that with a tenantId when retrieving from a repository:

class ProductRepository:
    public Product productOfId(TenantId aTenantId, ProductId aProductId)

Even the CQRS example, uses a combination of tenantId and entityId for its repositories single get method.

Its ubiquitous throughout, but I can’t understand why it would be necessary. If a product, backlog item, forum, calendar etc all have a globally unique identifier, why would you need anything more to query them?

If using this to ensure only entities for a particular tenant can be retrieved - this doesn’t seem to be a responsibility expected of a repository, but rather authentication etc, and so why include in every repositories query method?

Note, I understand why having tenantId as an attribute of the entity is required, and how it could be used to enforce some unique constraint. When retrieving a product however, wouldn’t productOfId(ProductId aProductId) suffice?

He does touch on this with “In the case of a multitenancy environment, the TenantId instance is also considered part of unique identity.” What value does combining two GUIDs to determine identity have over just the one?

Upvotes: 1

Views: 892

Answers (1)

Francesc Castells
Francesc Castells

Reputation: 2857

I don't think this is specific to DDD, but to multitenancy. Regardless of how you design your application, you need to be able to tell if a record or entity belongs to a tenant or another. So, even if the IDs are globally unique, therefore don't need another ID part for deduplication, you still need the tenant Id.

On a more practical note, you can find the utility of the tenant Id on every entity if you need to provide a restful API on top of that data. Most likely, your API structure will look like the following:

api/{tenantId}/entity-type/{entityId}

You will have to validate that the user doing the request has access to the given 'tenantId' based, for example, on the claims in the authentication token. If the user has access, then you'll read from the database. But, if your repository only accepts 'entityId', then it will return that entity regardless of the tenant it belongs to and a user from tenant1 could get data from any other tenant only knowing the Id. You can, of course, add a check of the tenant id after loading the entity, but sooner or later you will forget to add it. If instead, you follow the practice of always adding the 'tenantId' to your repositories, then this check is built into the queries themselves, making the whole process more consistent and efficient.

On a side note, there are other ways to add a tenant Id check on all your queries, which will achieve the same goal without having to manually pass it to every repository method call and query implementation. For example, you could put the tenantId on a context and inject it using DI resolving your Repository. With SQL Server, you could use row-level security, so that the tenantId check is part of a table policy, instead of adding it to all the queries on that table.

Upvotes: 3

Related Questions