【发布时间】:2023-03-11 00:41:01
【问题描述】:
我最近阅读了一些关于 CQRS 和事件溯源的文章。虽然第一个在我看来是一个非常复杂和冒险的解决方法,用于修复性能不佳的业务和设计不佳的数据访问层和数据模型,但最后一个似乎是许多问题的解决方案。
使用事件溯源解决的问题:
-
摆脱关系数据库和对象关系映射器,如 NHibernate 和实体框架。编程领域几乎没有人愿意关注诸如索引、表/索引碎片或规范化、如何设计关系数据以及如何编码/配置 ORM(其本身是一门科学)之类的东西。
李> 将业务模型和内存中的“数据库”结合在一起,一个实体/聚合服务将所有相关项保存在内存中,通过简单地将 CUD 事件转储到某个地方来保持完整性,而不会带来太大的痛苦。旧项目可以从内存中清除并转储到 NoSQL(或其他)存储并用于聚合计算、报告、搜索,并在必要时重新激活。如果我理解正确的话,像 VoltDB 这样的内存数据库以类似的方式使用事件转储,但仍然是关系数据库,与业务逻辑分离。
这也将使并发更容易:而不是锁定(可能会出现完整的系统死锁)或具有一般“成功或失败”逻辑的乐观锁定,具体取决于数据是否同时更改(或相当复杂的数据库代码),合并规则可以在代码中实现。
历史:实施审计功能、墓地表或“已删除”标记列或仍需要可能已删除的数据不再痛苦。
数据复制/搜索/报告:使用全文索引而不是追逐丢失的关系索引,创建适当的查看区域,以所需格式为用户准备数据,而不是在关系数据库中使用丑陋的复制例程,使用触发器、后续存储过程甚至程序代码将数据复制到六个不同的表。
版本控制:让许多模块运行多个不同的关系数据库版本是一件很痛苦的事情,每个版本都有不同的表和列,并且需要适当的 ORM 映射。在单层模型中可能更容易,事件转储接受任何对象格式(通常是无模式或松散模式 NoSQL 文档,表示为 JSON 或 XML)。也可以通过“数据架构更改事件”链升级旧数据(而不必维护关系数据库的迁移脚本)。
N 层业务模型/关系数据库/ORM 混乱
十年或更长时间前的 n 层方法可能是业务层和数据访问层。为了保持严格的分离,省略了许多关系特性,而是在业务层中实现它们:关系完整性,规范化,数据库是我所说的“垃圾转储”:看起来像一个在玩 SQL Server 的孩子管理工作室或访问。极度非规范化的多态引用(“外键”列引用不同的源表,由“ReferenceSource”标记标识),针对不同类型的业务对象滥用相同的表以及将数据复制到许多其他表(并从那里再次其他地方),因为性能不好,这应该可以改善查询。 ORM 的使用也没有对象引用,减少为单个对象加载和保存操作。加载聚合(实体/表行的图)将遍历图并为每组子实体发送查询。
当性能变得更差并且可能孤立引用造成严重问题时,可能会尝试实施经典的关系设计,但不可能使增长的系统适应完整的数据重新设计(没有人愿意为此付费),几乎没有人会知道如何在 ORM 中映射对象关系甚至优化加载。此类尝试仅限于设计中的少数几个地方,可能会使数据模型和访问更加难以维护。
n 层之上的 CQRS?
为了获得可接受的性能,可能会为某些模块构建单独的 SQL 查询,从而绕过具有单对象迭代访问的业务模型。这整个结构突然被称为事实上的 CQRS,因为单独的查询访问(可以通过良好实现的关系数据模型和 ORM 使用来处理,只要它不应该是“大数据" 类似 Google 或 Stackoverflow 的工作负载),以及关系表中的大量重复数据,弥补了应用程序的即时访问。
比不恰当的表格格式更好?
好的,所以我阅读了 CQRS,虽然我不喜欢前面描述的“CQRS”的使用,但事件存储而不是关系数据库的概念看起来非常有用:它不太可能成功地执行引入原始的、最先进的关系数据库设计和 OR 映射,即使它也将是极其昂贵的。事实上,普通的面向对象编程比大多数数据库表更“规范化”,因为需要将所有内容都压入表格式或为对象图/聚合创建大量表。我同意:必须手动处理搜索索引和碎片整理、模式管理和数据历史跟踪,就像石器时代的 IT,就像在现代汽车和电动高速列车之外运行福特 T 模型和蒸汽机车。
有什么好的经验吗?
使用事件溯源(不一定是完整的 CQRS)的体验如何?它是否消除了关系数据库的大部分痛苦?我真的很期待一种集成了所有业务逻辑的内存数据库,并且可能足够快以使单独的查询模块变得可有可无!
【问题讨论】:
标签: database orm cqrs relational event-sourcing