Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 作为聚合根的一部分加载所有实体_Java_Domain Driven Design_Ddd Repositories - Fatal编程技术网

Java 作为聚合根的一部分加载所有实体

Java 作为聚合根的一部分加载所有实体,java,domain-driven-design,ddd-repositories,Java,Domain Driven Design,Ddd Repositories,根据我在线阅读的内容,从数据库加载时,聚合必须处于完整状态。这意味着它必须能够访问从数据库加载的所有实体,以便聚合永远不会处于无效/不完整状态 如果聚合包含一个可能有数百万的实体(例如,聚合A包含实体B和实体C),该怎么办。现在,最坏情况下,聚合A下可能有多达100个实体B实例,但聚合A下可能有数百万(如果不是数十亿)的实体C 用例: 用例是,我想使用Id从聚合A中删除实体C的一个特定实例。要以DDD方式执行此操作,我必须首先从数据库中加载聚合A,并将其所有实体加载到内存中。然后使用如下方法移

根据我在线阅读的内容,从数据库加载时,聚合必须处于完整状态。这意味着它必须能够访问从数据库加载的所有实体,以便聚合永远不会处于无效/不完整状态

如果聚合包含一个可能有数百万的实体(例如,
聚合A
包含
实体B
实体C
),该怎么办。现在,最坏情况下,
聚合A
下可能有多达100个
实体B
实例,但
聚合A下可能有数百万(如果不是数十亿)的
实体C


用例:

用例是,我想使用Id从
聚合A
中删除
实体C
的一个特定实例。要以
DDD
方式执行此操作,我必须首先从数据库中加载
聚合A
,并将其所有实体加载到内存中。然后使用如下方法移除该项目:

public class AggregateA extends AbstractAggregateRoot<AggregateA>{

     private String aggregateId;

     private Map<String, EntityC> entityC;

     public void removeEntityC(String idToRemove) {
           this.entityC.remove(idToRemove);
           registerEvent(new EntityCRemoved(aggregateId, idToRemove));
     }
}
公共类聚合扩展了AbstractAggregateRoot{
私有字符串聚合ID;
私有地图实体;
public void removeEntityC(字符串idToRemove){
this.entityC.remove(idToRemove);
registerEvent(新的EntityRemoved(aggregateId,idToRemove));
}
}

问题:

将数百万个实体加载到内存中以对单个请求执行任何
写入操作(CQRS中的命令)
,这似乎不是正确的方法


我在这里遗漏了什么吗?

我认为这将是重新思考聚合边界的第一个迹象。一个可能的解决方案是将实体C视为聚合,并通过id引用聚合a。这取决于您的业务领域,因此这只是一个模糊的建议。您可以在聚合a中或在生成聚合C实例的单独域服务中使用工厂方法。通常,您应该避免在聚合中加载数百个实体,因为它将跨越该聚合周围的巨大事务边界

public class AggregateA {
    public AggregateC createAggregateC(...){
        //create AggregateC and return it with reference set to AggregateA
        return new AggregateC(this.id, ...)
    }
}
您误解了DDD的“加载整个集合”要求。就像你说的那样,这样做是完全不切实际的。我的建议是加载所需的聚合的完整部分

例如:我的行为(尽管是人为的)是在一个有数百万条评论的博客条目中编辑一条特定的博客评论。我要做的是加载博客聚合和我试图从存储库更新的单个评论。然后我会继续让博客聚合更新评论。当然,另一种设计选择是只加载注释(因为您可以用它的id标识它),但这将加载一个不完整的实体,因为它缺少聚合根(这是授权要求不要做的)


当然,这里的一个问题是在DDD中进行批量更新变得非常有效。这是一个设计上的弱点,也是DDD之外解决的问题。

但除了
聚合a
实体C
的数量外,它是
实体
的理想情况。其中一些是:1)没有聚合A的
实体C
是没有意义的。2) 如果删除
聚合A
则还必须删除所有关联的
实体C
。3) 对
实体C
的任何更改必须始终通过
聚合A
进行,因为在
实体C
上有一些由
聚合A
确保的不变量。将
实体C
分解为它自己的集合是没有意义的,这取决于您的业务逻辑。在一个聚合中加载数千个实体是没有意义的,因为事务边界将爆炸。1) 为什么不呢?你仍然会有他们之间的参考。2/3)您可以通过域服务强制执行这些不变量。我不确定,但我认为沃恩·弗农在这个博客系列中解决了你的一些问题。也许你可以看看那里。您在比例部分是对的,这就是问题的关键,“加载与聚合关联的实体的一百万个实例”没有意义。你能详细说明你的答案吗?因为我在任何地方都没有读过关于将一个聚合作为另一个聚合的一部分的参考资料,这确实比brckner的答案更有意义,但是现在如果假设你有一个域不变量,没有两个注释可以是相同的,那就不会有问题了(作为你答案的一个延续示例)。现在,当您编辑博客评论时,假设它与另一条评论冲突。因为您只加载了一条注释,所以除非依赖数据库进行唯一索引,否则您不知道它是否与另一条注释冲突。但DDD的目标是将所有不变量作为域的一部分,而不是依赖基础设施来确保不变量。好问题。在您的扩展示例中:您的UL现在要求博客下的评论是唯一的。因此,您的评论内容现在是一个关键。当你的博客(你的集合体)的水合物,你的回购需要采取的关键(内容),以及水合物。这是因为对于您尝试执行的功能,“完成”意味着使用现有注释键对博客和任何注释进行回复。因此,在运行blog.add(comment)之前,您就可以从repo获得一个带有单个注释的博客。剩下的就没什么了。我不太明白我是不是遗漏了什么?你能给我指一下我能理解的方向吗?水合物是指当你把它从仓库里拿出来的时候。因此,当您的博客文章从存储区(可能是数据库)中取出以添加评论时,还需要提取与新评论具有相同键的所有评论。