【问题标题】:CQRS or App Service?CQRS 还是应用服务?
【发布时间】:2012-09-19 07:15:28
【问题描述】:

所以我喜欢在我们的应用程序中使用 CQRS 的概念,主要是因为我们已经支持事件溯源(从概念上讲,不遵循您看到的任何规定)。然而,CQRS 似乎真的面向大数据、最终一致性之类的东西。我们总是会成为一个关系数据库应用程序,所以我不确定它是否适合。

我也有顾虑,因为我认为我需要在我的应用层做一些特殊的事情。在进行读取时,我需要强制执行安全性和过滤数据,这些事情传统上是在应用层中实现的。

我的第一个问题是,我的应用是否适合(传统的 MVC/关系数据库应用)?还是拥有一个传统的应用层并使用 DTO 映射器更有意义?

我的第二个问题是,从传统应用层向域模型发出命令是否有意义?我喜欢命令/命令处理程序和事件的想法。

让我澄清一下我的问题。我担心与授权相关的数据过滤。当用户请求数据时,必须有一个过滤器来限制对某些数据元素的访问,方法是将它们全部删除(因此它们不会返回给调用者)、隐藏值或对数据应用掩码。在一个人为的示例中,对于社会安全号码,发出请求的用户可能只能看到最后 4 个数字,因此结果将显示为 ###-##-1234。

我的断言是,这个责任在应用层。我认为这是一个方面,对查询或命令的所有响应都必须通过这种过滤机制。这就是我的 CQRS 天真的地方,也许是命令从不返回数据,只是指向通过读取模型查找的数据的指针?

谢谢!

【问题讨论】:

    标签: domain-driven-design cqrs


    【解决方案1】:

    首先:CQRS关系数据库 不会相互排斥。在高级场景中,将基于 SQL 的 DB 替换为其他存储方式可能是有意义的,但 CQRS 作为一个概念并不关心持久性机制。

    在依赖于角色和/或用户的多个视图的情况下,细读层可能应该提供多个结果集:

    1. 一个包含完整的 SSN,供有权访问该信息的用户使用。
    2. 另一种供无权查看该信息的用户使用
    3. ...

    这些可以存储在单独的数据存储中,但如果您使用单个基于 SQL 的数据库,它们也可以通过 SQL 视图提供。

    在 CQRS 中,Application Service 仍然以 Command Handlers 的形式存在。这些可以嵌套,即先处理授权,然后将命令发布到包含的命令处理程序。

    public class AuthorizationHandler {
    
        public CrmAuthorizationService(CrmCommandHandler handler) {
            _next = handler;
        }
    
        public void Handle(SomeCommand c) {
            if (authorized) _next.Handle(c);
        }
    }
    
    // Usage:
    var handler = new CrmAuthorizationService(new CrmCommandHandler());
    bus.Register<SomeCommand>(handler.Handle);
    

    这样你可以嵌套多个处理程序,例如作为 REST 信封,用于日志记录事务

    回答您的问题:

    第一: CQRS 是否适合您的应用?如果不真正深入研究具体要求,谁也说不出来。就 CQRS 的优缺点而言,仅仅因为您使用 MVC 和关系数据库并没有任何意义。

    第二:是的,在某些情况下,让您的应用层以经典方式与客户端交互并处理身份验证、授权等事情,然后在内部发出命令是有意义的.这在将基于 MVC 的 UI 或 REST API 置于应用程序之上时会很有用。

    根据评论更新:

    在理想的、纯粹的 CQRS 场景中,Sally 会为每个视图拥有自己的非规范化数据,例如NoSQL DB 中有几个名为 CustomerListForSallyCustomerDetailsForSally 等的文档。这些文档中填充了她可以查看的内容。

    一旦她升职——这将是一个重要的领域事件——她的所有非规范化数据都会被自动覆盖,并扩展为包含她现在可以看到的内容。

    当然,我们必须保持理性和务实,但这个理想应该是我们前进的大方向。

    实际上,您可能拥有某种基于用户/角色或用户/组的系统。为了能够查看敏感信息,您必须是特定角色或组的成员。这些中的每一个都可以有它们定义的视图和命令集。这不需要去噪数据它可以像 SQL-Views 一样简单:

    • CustomerDetailsForSupportStaff
    • CustomerDetailsForSupportExecutive 带有未屏蔽的 SSN
    • CustomerListForSupportStaff
    • CustomerListForSupportExecutive 包含客户总收入

    【讨论】:

    • 感谢您的好评。我对读取层的一个问题是,在应用程序中,确定数据授权的逻辑是在运行时计算的。例如,我今天可以以 Sally Smith 的身份登录,但她看不到社会安全号码。 Sally 获得了晋升,因此可以访问更敏感的信息。下次 Sally 登录时,她可以看到社会安全号码。授权确定实际上比这复杂得多。一般来说,我们先检索信息,然后过滤它,否则很难应用屏蔽。
    • 空间不足...这个授权逻辑在哪里?在薄读取层?必须有一些合作者将结果集和用户,查找适当的过滤器,将它们应用于结果集,并返回结果集,最终通过瘦读取层、应用服务、什么你。或者像这样的过滤是一个演示问题?
    • 再次感谢您的澄清,我非常感谢您的洞察力。在系统中,数据的屏蔽不是固定在某个角色上,而是通过配置和业务规则来完成的(即在运行时加载规则,将用户应用于规则和数据,选择性地屏蔽某些数据)。因此,提供 SQL 视图在我的特定用例中不起作用。当代码中的某处请求数据时,我必须从本质上过滤数据。抱歉,我没有从一开始就更清楚地说明这一点。那么回到我原来的帖子,这种行为在 CQRS 实现中会落在哪里?
    • 我喜欢您的见解并接受您的回答。鉴于安全性不是核心域的一部分,而是一个应用程序问题,我正在应用程序层实现安全性。我计划使用 AspectJ 来拦截请求并运行不同的授权检查。大多数请求的授权检查应该是相同的,所以它应该是一个相当优雅的解决方案。
    • @noplay 对我来说听起来很合理。很高兴我至少能提供一点帮助。
    猜你喜欢
    • 2012-03-21
    • 2018-12-30
    • 2011-01-29
    • 2013-09-01
    • 2016-12-23
    • 2018-08-12
    • 1970-01-01
    • 2014-03-04
    • 1970-01-01
    相关资源
    最近更新 更多