摘要
是否应按命令/查询实现CQRS / DDD中的授权?
我是第一次使用或多或少严格使用DDD CQRS模式的在线应用程序。我遇到了一个问题,我无法真正解决这个问题。
我正在构建的应用程序是分类帐应用程序,允许人们创建分类帐,也允许其他人查看/编辑/删除分类帐,例如员工。分类帐的创建者应该能够编辑他创建的分类帐的访问权限。甚至可以更改所有权。该域具有两个聚合TLedger和TUser。
我读了很多有关DDD / CQRS关键字的文章,涉及安全性,授权等。大多数人说授权是通用子域,除非正在构建安全应用程序。
在这种情况下,核心域当然是对交易,余额和帐户感兴趣的会计域。但是还需要能够管理对分类帐的细粒度访问的功能。我想知道如何用DDD / CQRS术语进行设计。
在DDD教程中,所有命令都是通用语言的一部分。它们很有意义。它们是代表“真实事物”的具体动作。
因为所有这些命令和查询都是用户将在“现实生活”中执行的实际操作,所以授权的实现是否应与所有这些“命令”和“查询”结合使用?例如,用户将具有执行TLedger.addTransaction()的权限,但没有执行TLedger.removeTransaction()的权限。或者,将允许用户执行查询“ getSummaries()”,但不执行“ getTransactions()”。
三维映射将以用户分类帐命令或用户分类帐查询的形式存在,以确定访问权限。
或者,以分离的方式,将为用户注册名为“权限”的权限。然后将为特定命令映射权限。例如,权限“ ManageTransactions”将允许用户执行“ AddTransaction()”,“ RemoveTransaction()”等。
权限映射用户->分类帐->命令/查询
权限映射用户->分类帐->权限->命令/查询
那是问题的第一部分。或简而言之,CQRS / DDD中的授权应按命令还是按查询实施?还是应该将授权与命令分离?
其次,关于基于权限的授权。用户应该能够管理其分类帐或允许其管理的分类帐上的权限。
- 授权管理命令在分类帐中发生
我想到了将事件/命令/处理程序添加到Ledger聚合中,例如GrantPermission(),revokePermission()等。在这种情况下,强制执行这些规则将发生在命令处理程序中。但这将要求所有命令都包含发出该命令的用户的ID。然后,我将检查到TLedger中是否存在该用户执行该命令的权限。
例如 :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- 用户中的授权管理命令
另一种方法是将权限包括到TUser中。TUser将具有一组权限。然后,在TLedger命令处理程序中,我将检索用户并检查他是否有权执行该命令。但这将要求我为每个TLedger命令获取TUser聚合。
class TAddTransactionCmdHandler(cmd) {
this.userRepository.find(cmd.userId)
.then(function(user){
if (!user.can(cmd)){
throw new Error('Not authorized');
}
return this.ledgerRepository.find(cmd.ledgerId);
})
.then(function(ledger){
ledger.addTransaction(cmd);
})
}
- 另一个服务域
另一种可能性是完全建模另一个授权域。该域将对访问权限,授权等感兴趣。然后,计费子域将使用服务以的形式访问此授权域AuthorizationService.isAuthorized(user, command)
。
class TAddTransactionCmdHandler(cmd) {
authService.isAuthorized(cmd)
.then(function(authorized){
if (!authorized) throw new Error('Not authorized');
return this.ledgerRepository.find(cmd.ledgerId)
})
.then(function(){
ledger.addTransaction(cmd);
})
}
什么决定是最“ DDD / CQRS”方式?