DDD CQRS-每个查询和每个命令的授权


15

摘要

是否应按命令/查询实现CQRS / DDD中的授权?

我是第一次使用或多或少严格使用DDD CQRS模式的在线应用程序。我遇到了一个问题,我无法真正解决这个问题。

我正在构建的应用程序是分类帐应用程序,允许人们创建分类帐,也允许其他人查看/编辑/删除分类帐,例如员工。分类帐的创建者应该能够编辑他创建的分类帐的访问权限。甚至可以更改所有权。该域具有两个聚合TLedgerTUser

我读了很多有关DDD / CQRS关键字的文章,涉及安全性,授权等。大多数人说授权是通用子域,除非正在构建安全应用程序。

在这种情况下,核心域当然是对交易,余额和帐户感兴趣的会计域。但是还需要能够管理对分类帐的细粒度访问的功能。我想知道如何用DDD / CQRS术语进行设计。

在DDD教程中,所有命令都是通用语言的一部分。它们很有意义。它们是代表“真实事物”的具体动作。

因为所有这些命令和查询都是用户将在“现实生活”中执行的实际操作,所以授权的实现是否应与所有这些“命令”和“查询”结合使用?例如,用户将具有执行TLedger.addTransaction()的权限,但没有执行TLedger.removeTransaction()的权限。或者,将允许用户执行查询“ getSummaries()”,但不执行“ getTransactions()”。

三维映射将以用户分类帐命令或用户分类帐查询的形式存在,以确定访问权限。

或者,以分离的方式,将为用户注册名为“权限”的权限。然后将为特定命令映射权限。例如,权限“ ManageTransactions”将允许用户执行“ AddTransaction()”,“ RemoveTransaction()”等。

  1. 权限映射用户->分类帐->命令/查询

  2. 权限映射用户->分类帐->权限->命令/查询

那是问题的第一部分。或简而言之,CQRS / DDD中的授权应按命令还是按查询实施?还是应该将授权与命令分离?

其次,关于基于权限的授权。用户应该能够管理其分类帐或允许其管理的分类帐上的权限。

  1. 授权管理命令在分类帐中发生

我想到了将事件/命令/处理程序添加到Ledger聚合中,例如GrantPermission(),revokePermission()等。在这种情况下,强制执行这些规则将发生在命令处理程序中。但这将要求所有命令都包含发出该命令的用户的ID。然后,我将检查到TLedger中是否存在该用户执行该命令的权限。

例如 :

class TLedger{ 
    function addTransactionCmdHandler(cmd){
        if (!this.permissions.exist(user, 'addTransaction')
            throw new Error('Not Authorized');
    }
}
  1. 用户中的授权管理命令

另一种方法是将权限包括到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);
    })

}
  1. 另一个服务域

另一种可能性是完全建模另一个授权域。该域将对访问权限,授权等感兴趣。然后,计费子域将使用服务以的形式访问此授权域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”方式?


1
很好的问题-我一直在尝试解决类似的问题,而且没有文献似乎直接解决这个问题。您的问题的后半部分让我有些困惑。听起来您好像想知道权限管理的位置(添加或删除权限),但是显示的示例用于添加事务,因此看起来更像是后半部分在问“我应该如何查询权限”。你能澄清一下那部分吗?
emragins

每个事务都可以具有执行策略。每个用户应属于一个或多个组,每个组将具有一个访问配置文件,用于指定允许哪些交易。在运行时,在执行事务之前,将针对执行用户对聚合的配置文件进行检查。当然,这说起来容易做起来难。
NoChance

Answers:


5

对于第一个问题,我一直在努力解决类似问题。我越来越倾向于采用三阶段授权方案:

1)在命令/查询级别的授权为“此用户是否曾经有权执行此命令?” 在MVC应用程序中,这可能可以在控制器级别进行处理,但是我选择了一个通用的预处理程序,该处理程序将根据当前用户和正在执行的命令来查询权限存储。

2)应用程序服务内部的授权“此用户“是否*有访问该实体的权限吗?”基本上是一个TenantId,具有更多的OrganizationId粒度。

3)依赖于您实体的瞬时属性(例如状态)的授权将在域内部进行处理。(例如,“只有某些人可以修改封闭的分类帐。”)我选择将其放入域中,因为它严重依赖于域和业务逻辑,而在其他地方我不太愿意将其公开。

我很想听听其他人对这个想法的回应-如果您愿意,可以将它撕成碎片(如果您愿意,请提供一些替代方法:))


我认为您对授权的不同“层次”有一些合理的观点。我正在使用的系统具有不同类型的用户-注册用户和工作人员。命令/查询处理程序权限对用户类型进行了基本检查。如果是员工,那总是会经历。如果它是注册用户,则仅在满足某些条件(例如,对汇总的权限)时才被允许。
magnus

0

我会将授权作为您的Authorization BC的一部分实施,但将其作为操作筛选器部署到Ledger系统中。这样,它们可以在逻辑上彼此分离-您的账本代码不必调用授权代码-但您仍然可以对每个传入请求获得高性能的进程内授权。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.