cqrs
CQRS的最大好处之一是能够实现多个读取模型。 业务规则和域模型是安全,整洁且在写模型中隔离的。 他们并没有妨碍视图模型的使用,视图模型可以选择性地选择他们感兴趣的部分,自由地重塑它们,并以需要与领域模型不同的优雅和清晰度的方式来做所有事情。 读取模型都是关于查询性能和便利性的。
简而言之,CQRS是Pat Helland在其关于不变性的论文中所描述的实际实现: 事实就是对数。 数据库是日志子集的缓存。 让我们看一下这种方法的一些后果。
关系数据库中的持久模型
实现读取模型的最明显的方法也许是在传统SQL数据库中 。 这项技术已经存在了数十年,确实很成熟并且经过了实战测试,并且每个人都熟悉它。
但是,在CQRS世界中,我们可以执行在典型的应用程序数据库模式中可能会出现问题的操作。 由于我们优化了读取的便利性和性能,因此数据经常会被非规范化 。 它可以通过多种方式发生:
- 可能存在将来自其他某些字段的数据组合在一起的字段(例如,具有人类友好街道地址的单个文本字段)。
- 同一数据可能存在于多个地方,一个以上的表中。 例如,将人们可读的街道地址放在一列中可能很有意义,但同时将州和城市保留在不同的列(或表格)中。
- 有时保留多个修订或更改实体的历史记录(不仅仅是最终版本)也很有意义。
- 非规范化数据的另一个很好的例子是分析(例如OLAP多维数据集)。
- 数据不必是关系的。 您不需要将Java对象中的字段映射到列,也不需要将对象图映射到许多表。 您可以将整个序列序列化(转换为JSON,XML,使用本机Java序列化等),然后将其放入blob字段中。
为什么要非正规化?
您可能想知道为什么首先要使用非规范化架构。 答案是:在处理事件且没有人等待答案的情况下,提前进行更多计算。 这意味着在人们等待答案的那一刻,计算量会减少。
原因是人的时间很昂贵,而且越来越贵,而计算能力和存储却已经非常便宜,甚至越来越便宜。 可能需要提前进行大量计算,以节省一个人的时间。
多少非正规化?
非规范化的程度主要取决于性能和查询复杂性。 完全规范化的模式有其优点,但也有许多缺点。 许多连接,计算和过滤器很快变得难以编写和维护。 它们也可能成为性能梦night,例如在大型表之间进行联接。 即使您没有联接成千上万的行,非平凡的计算也可以使用户等待。
可以使用非规范化为查询准备答案。 如果查询需要通常存储在几个大表中的数据,则可以将它们组合一次(以异步投影方式),然后在用户需要时以恒定或对数时间进行查询。 甚至有可能达到极限并预先计算对所有常见查询的响应,从而消除了对高级缓存的需求。 在这种情况下,视图模型是缓存 。
不过,有必要在这里寻求平衡。 过度积极的非规范化会导致与代码复制相关的可维护性差,并增加数据的绝对数量(以字节为单位)。
其他永久性解决方案
如果数据不必是关系数据,或者可以将数据进行非规范化,则最好将其放入其他类型的数据库中。 NoSQL选项种类繁多,最明显的选择是文档存储和键值存储。
但是,我们不必到此为止–如果数据可以从图形数据库中受益,那么就没有障碍。 视图模型的另一个很好的例子是像Lucene这样的搜索索引。
这样的商店经常有其缺点。 他们可能会在可用性和性能的一致性方面进行权衡。 它们可能非常专业,或仅限于特定模型(图形,文档,键值等)。 它使它们成为典型的非CQRS读/写模型中的主要持久性机制,甚至具有挑战性甚至不适用。 但是,它们在CQRS视图模型中可能是完全可以接受的,其优点使整个功能更加强大。
在记忆中
我们一直在考虑的另一个想法是内存模型。 写入磁盘和从磁盘读取速度很慢,并且如果数据适合RAM ,为什么不以您选择的语言将其保存在内存中,也可以保存在普通数据结构中?
有一些挑战:
- 如果事件存储足够大,则读取和使用它可能会花费相对较长的时间并占用大量资源。 这个限制可能比最初看起来要遥远,但这当然是必须仔细考虑的事情。
- 它需要是事务性的。 查询读取数据时更改数据是不可接受的。 您可能还需要回滚,这绝非易事。 在支持事务性内存或持久性数据结构(如Clojure)的语言中,这要容易得多,并且您可能需要在其他地方具有这种功能的库。
这些挑战可以通过使用持久性事务存储来解决:
- 使用域事件时,请更新内存模型。 不要触摸磁盘。
- 时不时地(例如每1000个事件或每分钟)拍摄该模型的快照并将其写入一些持久性存储中。
- 让查询从该持久快照读取,可能将其缓存在内存中。
- 重新启动应用程序或出现错误后,请继续使用最新快照中的事件。
它接近持续的预测,但是有重要的区别。 在这种情况下,持久性仅用于隔离以及重新启动后从保存点恢复的方式。 磁盘IO可以异步发生,也可以不频繁发生,而不会减慢编写器和查询的速度。
资料保留
大多数查询仅对相对最新的数据感兴趣。 有些人可能需要一两年,而其他人可能只对最后一周感兴趣。 由于源数据在域侧是安全的,因此读取模型可以自由保留所需的内容。 它可以对其性能和存储要求产生巨大的积极影响。
也可能有许多具有相同模式但数据保留不同的模型。 尽可能使用较小的数据集以获得最佳响应。 但是对于偶尔询问遥远的过去(仍然可以接受更长的响应时间),仍然可以使用较大的数据集。
该方法可以与不同的粒度结合使用:将所有详细信息保留在过去几周或几个月内,并在更长的时间内进行汇总或缩小。
结语
NoSQL存储,分析,搜索索引,缓存等都是非常流行和有用的工具,并且经常以类似于CQRS的方式使用它们,而无需对其进行确认。 无论是否使用触发器,消息传递,轮询或ETL填充它们,最终结果都是对数据的新的,专门的只读视图。
但是,项目越成熟,规模越大,引入此类内容就越困难。 它可能变得非常昂贵,失去的机会最终会导致许多问题。
如果从一开始就拥有CQRS,那就容易得多 。 域模型以及其他最终数据源(例如事件存储库)在其他地方保持安全和整洁。 可以轻松获取这些数据以供使用(尤其是在事件源中)。 衍生视图模型所需的全部工作是将另一个使用者插入域事件。
视图模型也非常适合创新。 尝试各种数据库和编程语言以及使用相同工具解决问题的不同方法确实很容易。
该帖子还出现在Oasis Digital博客上 。
翻译自: https://www.javacodegeeks.com/2015/09/persistence-in-cqrs-read-models.html
cqrs