我想了解为什么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有什么价值?
我认为这不是针对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检查成为表策略的一部分,而不是将它添加到该表的所有查询中。