oop - 事件溯源中的值对象
问题描述
在事件源域模型中是否有值对象的位置?
让我们将值对象定义为具有不可变状态的对象,该对象保护其不变量并且没有特定标识符。
在这种情况下,事件源域模型是完全或部分事件源的域,这意味着它的当前状态可以通过应用过去发生的所有事件得出。即使随着时间的推移,事件本身也被认为是不可变的。
关于在事件中使用值对象的有效性已经发生了争论——这个问题稍微进一步:值对象在事件源域中是否有一席之地?
使用值对象的(潜在)问题是,以不变量被收紧的方式改变域变得相当棘手。
这种情况的一个示例是有一个Username
值对象,唯一的约束是名称必须介于 2 到 16 个字符之间。
虽然这在一段时间内运行良好,但该公司决定只允许至少 5 个字符的用户名。迁移期开始,名称少于 5 个字符的用户被要求更新其名称。
可以说该过程是成功的,应用了更正事件并且每个人都很高兴。我们收紧对Username
值对象的约束,要求至少 5 个字符。
有一段时间每个人都很高兴,但随后我们发现快照存在问题并重播所有事件。
我们现在面临Username
对象的一个异常:通过加载历史数据,我们破坏了域的不变量。
值对象的规则具有追溯性——这是否使它们天生不适合事件溯源?是否值得应用值对象的版本控制?有没有更简单的方法来避免此类问题?
解决方案
我想说,在您重新定义Username
含义的那一刻,并且您没有以某种方式迁移历史数据,您实际上已经创建了 2 个不同的Username
含义。
因为这个词有 2 种不同的含义,所以你必须以某种方式在代码中明确说明。“版本控制”是一种方式,虽然我不会使用这样的通用解决方案,但有不同的建模选项。
您可以明确表示“用户名”的历史就是这样,历史。因此,例如创建一个HistoricUsername
,它是事件源对象,如果您愿意,甚至是一个值对象。并创建一个Username
始终是具有最新规则的用户名,它根本不会持久化,但HistoricUsername
如果可以的话,它是从 a 创建的。
有些人建议有时从对象中提取“规则”,然后再重新应用。这样,对象本身在任何时候都是有效的,您可以要求它根据可能发生变化的规则来验证自己。我真的不喜欢这些解决方案,但它是一种选择,并且Username
仍然是一个价值对象。
所以问题并不在于价值对象不适合事件溯源,而只是建模必须更准确。
推荐阅读
- spring-boot - 在springboot中生成YAML格式响应
- mysql - 如何在一个查询中从三个表中获取数据,其中表 2 包含表 1 和表 3 中的外键
- spring-boot - 是否值得在 Spring-boot 应用程序中使用 AOP?
- azure - Azure函数获取http触发器的路径
- python - 如何使用python将我的构建参数作为键和值写入文件
- javascript - 单击另一个音频元素时暂停音频
- java - 如何创建每次触发事件时处理的反应流
- android - ListView内的Android约束布局 - 最后一项样式被破坏
- swift - 架构 x86_64 的未定义符号:“_opt”
- docker - 是否可以在 Windows 上本地运行 docker 映像(基于 Linux 容器)