如果对这个问题有一些单一思路的话,我会试着在书本上玩,并且很难掌握......
让我说我平常ShoppingCart
。当用户浏览商店时,他可以将ShoppingCartOrderLines
添加到购物车。每个步骤都保存到DB。让我们想象一下,无论出于何种原因,用户可以在每次购物会话中花费最多金额,这意味着所有ShoppingCartOrderLines
的总和不能超过一定金额。
所以,ShoppingCart
将是我的AggregateRoot。现在,在添加新的ShoppingCardOrderLines
时,我首先需要从DB中提取现有的,然后创建聚合根,然后在该实例上调用AR.AddItemToCart(Item)
。
如何从DDD角度完成此流程?
我是否应该将存储库拉出给定Cart的所有ShoppingCartOrderLines
并将它们添加到循环相同AR.AddItemToCart(Item)
方法的AT实例(这在某些域服务中完成)?或者我的存储库是否应该直接返回充满来自DB的所有数据的AR?还是第三种方式?也许有专门的构造函数,其中一个参数是已经保存在Db中的Items
集合?
我想找到一些一致的方法,因为如果我最终阅读读取方面的整个聚合根,可能会非常快速地变得非常大/复杂。另外,根据我在几个地方阅读的内容,执行规则(AR.AddItemToCart(Item)
中的规则)应该只在写入方面完成,这意味着当从DB读取它时,应该以其他方式完成。
答案 0 :(得分:1)
如何从DDD角度完成此流程?
通常的模式遵循Blue Book中的描述:存储库提供Root的实例。
这里的部分要点是,应用程序和域模型都没有对聚合如何存储的细节感兴趣或投资 - 存储库将其他组件与是否持久表示的知识隔离开来数据是RDBMS中的行,文档存储中的文档,或者只是磁盘上的字节。
这通常意味着存储库需要类似构建器/工厂(由域模型实现),以便它可以将持久化表示转换为内存表示中的(当前)域模型。
如果我们假设域模型没有设置器那么意味着存储库实际上通过通常的域接口(方法)来设置实例,这实际上意味着在读取端再次强制执行所有规则?
在这里要认识到一件重要的事情:阅读方根本不应该执行任何规则。这就是写作模型的工作。
但是:如果您需要将域不可知字节转换为域模型值,或将未经验证的值对象转换为验证值对象;这些转换的逻辑由域模型提供。
答案 1 :(得分:1)
DDD相对没有规定性。它更多地取决于你的背景,约束和自己的偏好。
我是否应该将存储库拉出给定的所有ShoppingCartOrderLines 购物并将它们添加到AT实例循环相同 AR.AddItemToCart(Item)方法(这在某个域中完成 服务)?或者我的存储库应该返回充满所有数据的AR DB直接?
前者是您在Event Sourcing场景中所做的。您 根据相同的聚合转换函数重放所有事件 前面的应用程序将使用并最终得到一个 最新加载的Aggregate。
后者是更传统的基于国家的情况 像RDBMS一样持久化。在这里,通常会进行聚合补液 通过与前向域操作完全不同的路径 因为你一次性加载所有内容,基本上绕过域。您不必进行连续的小变换,其中必须检查不变量。 存储库应该能够自主查询数据库并将数据转换为完整的聚合。在保留实体封装的同时,有不同的技术可以实现这一点 - 允许持久层访问保护级别,允许它修改通常只对域可用的字段,创建实体可以"吸收"等