实现领域驱动设计。为什么在所有版本库查询中都包含TenantId?

问题描述 投票:1回答:1

我想了解为什么Vaughn Vernon(在红皮书的Github Sample代码中)在每个版本库get或find方法中都包含tenantId。尤其是那些做基本的getById的方法。

在一个例子中,他用username来提供User的身份,而业务规则是用户名在一个tenancy中必须是唯一的,所以。

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

所以:"很合理

但是在他的其他BC中,他使用的是基于GUID的身份值对象,但是在从资源库中检索时,仍然将其与tenantId结合起来。

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

即使是CQRS的例子,也使用了tenantId和entityId的组合,用于存储库的单一获取方法。

它在整个过程中无处不在,但我不明白为什么有必要这样做。如果一个产品,积压项目,论坛,日历等都有一个全球唯一的标识符,为什么你还需要更多的东西来查询它们?

如果使用这个来确保只有特定租户的实体可以被检索--这似乎并不是一个版本库应该承担的责任,而是身份验证等,所以为什么要包含在每个版本库的查询方法中?

请注意,我理解为什么需要将 tenantId 作为实体的属性,以及如何使用它来执行一些独特的约束。然而,当检索一个产品时,productOfId(ProductId aProductId)不是已经足够了吗?

他确实用 "在多租户环境中,TenantId实例也被认为是唯一身份的一部分 "来触及这个问题。将两个GUID结合起来确定身份比只用一个GUID有什么价值?

domain-driven-design repository-pattern identity
1个回答
2
投票

我认为这不是针对DDD的,而是针对多租户的。无论你如何设计你的应用程序,你都需要能够判断一个记录或实体是属于一个租户还是另一个租户。因此,即使ID是全局唯一的,因此不需要另一个ID部分来进行重复数据删除,你仍然需要租户Id。

从更实际的角度来看,如果你需要在这些数据之上提供一个restful API,你可以在每个实体上找到租户Id的效用。最有可能的是,你的API结构会像下面这样。

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

你必须验证进行请求的用户是否可以访问给定的 "tenantId",例如,基于认证令牌中的要求。如果用户有访问权限,那么你将从数据库中读取。但是,如果你的存储库只接受'entityId',那么它将返回该实体,而不管它属于哪个租户,来自tenant1的用户可以从任何其他租户那里获得数据,只知道Id。当然,你可以在加载实体后添加对租户id的检查,但你迟早会忘记添加。如果相反,你遵循总是将 "tenantId "添加到你的存储库中的做法,那么这个检查就会被内置到查询本身中,使整个过程更加一致和高效。

另外,还有其他方法可以在你的所有查询中添加 tenant Id 检查,这将实现同样的目标,而不必手动将其传递给每个存储库方法调用和查询实现。例如,你可以把tenantId放在一个上下文上,并使用DI解析你的Repository来注入它。在SQL Server中,你可以使用行级安全,使tenantId检查成为表策略的一部分,而不是将它添加到该表的所有查询中。

© www.soinside.com 2019 - 2024. All rights reserved.