加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

域驱动设计 – DDD – 如何补水

发布时间:2020-12-14 04:53:41 所属栏目:百科 来源:网络整理
导读:Question : what is the best,efficient and future proof way to rehydrate an aggregate from a repository? What are the pro’s and con’s of the provided ways and are my perceptions correct? 假设我们有一个带私有setter的Aggregate Root,但是有一

Question: what is the best,efficient and future proof way to
rehydrate an aggregate from a repository? What are the pro’s and con’s of the provided ways and are my perceptions correct?

假设我们有一个带私有setter的Aggregate Root,但是有一个用于访问状态的公共getter

行为是通过聚合根上的方法完成的.

指示存储库加载聚合.

目前我看到了几种可能的方法来实现这一目标:

>通过反射设置状态(手动或自动,例如.
Automapper)
> make构造函数接受属性,以便设置状态
>使用state object加载聚合

1)Jimmy Bogard暗示他的工具Automapper不适用于双向映射.但有些人认为我们必须务实,以一种帮助你的方式使用工具.

对我来说,我不喜欢通过反射完全补液.也许Automapper存在,或者聚合根以这样的方式进行映射(参见Vaughn关于他的文章的一些评论).

2)创建用于补液的构造因子,具有几个参数,因此聚集体的状态以正确的方式再水合.

这几个参数可以扩展(=新构造函数)或定义可以更改.我喜欢这种方法,除了有一堆参数的部分.

3)状态是聚合根的属性.状态被封装在一个新对象中,该对象由存储库构建,然后被提供给聚合根以获得正确的init.

有些人认为构建这个状态对象是更多的工作(新类,实体上的状态属性暴露和聚合根来强制执行业务规则),但它提供了一种初始化状态的简洁方法.

假设我们需要事件源,加载状态是否类似于加载事件?状态对象是否提供了处理事件的方法?它是未来的证据吗?

解决方法

我认为,尝试面向未来的过多代表了许多人陷入的陷阱,这增加了代码库的复杂性.在良好的架构决策和过度架构解决问题的解决方案之间存在良好的平衡行为,而这种解决方案并不能保证存在.

话虽这么说,我完全同意吉米所说的,关于AutoMapper不是用于双向映射.您的域名代表您的应用程序中的“真相”,不应该直接可变.我曾经研究过具有双向映射的项目,虽然它们确实有效,但仍然倾向于将域对象视为DTO.当你开始拥有只读属性时,它会变得很痛苦,不得不反思你的设置 – 工具与否.从DDD的角度来看,我们不应该允许外部影响简单地说出属性值应该是什么,因为它将导致一个贫血的领域模型随着时间的推移,最有可能.

内部状态确实运行良好,但它们以额外开销和复杂性为代价.正如你所提到的那样,有一个合理的权衡,因为你正在增加相当多的工作量.但是,您可以利用该机会允许聚合在允许设置状态之前根据聚合中的自包含业务规则验证状态.这解决了我对双向映射的最大担忧.您至少可以强制执行状态对象包含有效数据,然后仅在聚合有效时构造聚合.它也更容易测试.我在这种方法中遇到的最大问题是,团队的技能水平将直接影响到正确使用这种方法的成功.可以说,复杂性并没有为实现域范围增加足够的价值,因为您可能会有具有不同流失水平的聚合.我参与的一些项目使用了这种方法,而且我发现直接构造函数的使用没有什么优势.

通常,在大多数情况下,我使用构造函数进行补液.它在不过度复杂之间走了一条路,加上它使聚合体承担允许或不允许构造对象的责任 – 再次允许域控制水合尝试是否会产生有效对象.对构造函数膨胀的一个很好的折衷是使用可变DTO作为构造函数的参数,本质上充当数据结构以随时间维护一致的构造函数签名.在这个本质上,它也有点面向未来.它采用状态对象方法最有吸引力的特权,即干净的签名,但删除了内部抽象的附加层.

你提到事件采购作为一种可能性.状态加载与你将要做的完全不同(在我看来).使用状态对象,您可以在给定时间点快照聚合的状态.通过事件源,您将重放事件,每个事件代表改变状态所需的数据,而不是状态本身.因此,您的构造函数可能是事件的集合,表示重复变异状态的一系列增量,直到达到当前状态.当你想要为你的聚合提供水合时,你将为它提供与该聚合相关的事件,并且它将重放它们以达到当前状态.这也是事件采购的真正优势之一.您每次都强制域对象的水化以完成创建它们所需的业务逻辑.给定事件列表,聚合将通过以一致的方式应用事件来强制执行每个状态更改,无论事件是实时应用还是重放以达到当前状态.

回到面向未来的方面,因为它涉及事件源,当事件需要改变时需要有意识的努力.由于您必须重放事件才能进入当前状态,因此您很可能不得不弃用事件并将新事件转换为业务逻辑更改.您可以(读作“可能会”)发现自己的版本化事件.您的聚合不仅需要了解当前的状态更改要求,还需要了解以前的状态更改要求.因此,如果更改事件处理程序,则必须确保它对现有事件也有效.在向事件添加其他数据时,通常不会涉及太多.但是,当您开始从事件签名中删除数据时,您会立即使该事件面临与早期结构不兼容的风险.同样,即使更改事件内部的数据结构的名称也可能导致向后兼容性问题.如果您开始采购事件,则无需担心与向后兼容性相关的未来验证.事件采购很棒,但要做好准备,以增加复杂性.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读