代码之家  ›  专栏  ›  技术社区  ›  DiscoStu

使用来自其他聚合的数据检查命令的有效性

  •  4
  • DiscoStu  · 技术社区  · 8 年前

    我目前正在开发我的第一个更大的DDD应用程序。目前,它运行得很好,但从早期开始,我就一直在思考一个问题:

    在我们的一些aggreates中,我们保留对另一个聚合根的引用,这对整个应用程序非常重要(基于它们的ID,因此没有硬引用-删除也是基于事件/最终一致性)。现在,当我们创建一个新实体“Entity1”时,我们将发送一个新的CreateEntity1Command,其中包含引用的聚合根的ID。

    现在,如何检查引用的ID是否有效?现在,我们通过读取另一个聚合来检查它(没有修改那里的任何内容),但这种方法不知何故感觉不好。我只想“信任”这些命令,因为不能手动输入ID,但必须选择。问题是,我们的应用程序是一个web应用程序,信任您在那里得到的用户输入并不是真正安全的(即使公众无法访问)。

    我是否忽视了这些问题的任何可能的解决方案,还是应该忽视需要更好的解决方案的感觉?

    3 回复  |  直到 8 年前
        1
  •  6
  •   Constantin Galbenu    8 年前

    验证是否存在另一个引用聚合不是聚合的责任。它会打破 Single responsibility principle . 当 CreateEntity1Command 到达聚合时,应考虑其他引用聚合处于有效状态,即 存在 .

    由于在聚合边界之外,此检查最终是一致的。这意味着,即使最初通过,也可能在之后失效(即 deleted , unpublished 或任何其他无效的域状态)。您需要确保:

    • 如果引用的聚合尚不存在,则该命令将被拒绝。在使用域服务将命令调度到聚合之前,在负责用例的应用程序服务中执行此检查。

    • 如果引用的聚合随后进入无效状态,则会采取更正操作。您应该在传奇/流程管理器中这样做。如果使用CQRS,则您订阅相关事件;如果不是,则使用 cron . 什么是正确的操作取决于您的领域,但主要思想是应该将其建模为一个过程。

    长话短说, 骨料的责任不超出其稠度边界 .

    P、 抵制将服务(域或非域)注入聚合(通过构造函数或方法参数)的诱惑。

        2
  •  2
  •   Kanishk    8 年前

    直接骨料相互作用是DDD中的一种反模式。聚合A不应直接向聚合B发送命令或查询。聚合是严格的一致性边界。

    我可以为您的问题想出两种解决方案:假设您有2个聚合根(AR)-A和B。每个AR都有一组命令处理程序,其中每个命令引发1个或多个事件。A中的命令处理程序依赖于B中的某些数据。

    1. 您可以订阅B引发的事件并在A中保持B的状态。您只能订阅指示有效性的事件。

    2. 您可以让a和B之间有一个完全独立的服务进行协调。不要直接将您的请求发送给a,而是将您的请求发送给S,S将负责B的查询(检查引用ID的有效性),然后将请求转发给a。这有时称为流程管理器(PM)。

    例如,在您创建新实体“Entity1”时,将此请求发送给PM,PM的任务是验证您请求中的数据是否有效,然后将您的请求路由到负责创建“Entity1”的聚合。将包含引用聚合根ID的新CreateEntity1Command发送到此PM,该PM使用引用AR的ID来确保其有效,如果有效,则只有它将转发您的请求。

    有用的链接: http://microservices.io/patterns/data/saga.html

        3
  •  1
  •   VoiceOfUnreason    8 年前

    我是否忽视了这个问题的任何可能的解决方案

    你做到了。“域服务”为您提供了一个可能的循环漏洞。

    骨料是一致性边界;他们的行为受到

    • 骨料的当前状态
    • 它们被传递的参数。

    如果聚合需要与边界外的某个对象进行交互,则传递给聚合根a 域服务 封装该交互。聚合可以自行决定调用域服务提供的方法来实现工作。

    通常,域服务只是应用程序或基础结构服务的包装。例如,如果聚合需要知道一些外部数据是否可用,那么您可以传入一个支持该查询的域服务,并对照一些数据缓存进行检查。

    但是,诀窍在于:您需要了解这样一个事实,即来自聚合边界之外的数据是必要的 不新鲜的 . 即使您正在查询过时的副本,也可能会有另一个过程更改数据。

    问题是,我们的应用程序是一个web应用程序,信任您在那里得到的用户输入并不是真正安全的(即使公众无法访问)。

    没错,但这通常不是域问题。例如,我们可以指定API中的端点需要某个命令消息的JSON表示,但这并不意味着域模型负责获取原始字节数组并为其创建DOM。应用层将承担该责任;聚合的责任是域关注点。

    要区分不同关注点之间的边界在哪里,可能需要仔细考虑。这个字节序列是a吗 有效的 聚合的标识符?显然是一个应用程序问题。另一个聚合是否处于允许某些行为的状态?显然是一个领域问题。聚合是否存在。。。?可以走任何一条路。

    推荐文章