当前位置:  开发笔记 > 前端 > 正文

使用RDBMS作为事件源存储

如何解决《使用RDBMS作为事件源存储》经验,为你挑选了2个好方法。

如果我使用RDBMS(例如SQL Server)来存储事件源数据,架构可能是什么样的?

我从抽象的意义上看到了一些变化,但没有具体的.

例如,假设有一个"产品"实体,对该产品的更改可以采用以下形式:价格,成本和描述.我很困惑我是否:

    有一个"ProductEvent"表,其中包含产品的所有字段,其中每个更改表示该表中的新记录,以及"适当的人员,内容,地点,原因,时间和方式".当成本,价格或描述发生变化时,会添加一个全新行来表示产品.

    将产品成本,价格和描述存储在使用外键关系连接到Product表的单独表中.当发生对这些属性的更改时,请根据需要使用WWWWWH写入新行.

    在"ProductEvent"表中存储WWWWWH以及表示事件的序列化对象,这意味着必须在我的应用程序代码中加载,反序列化并重新播放事件本身,以便为给定产品重新构建应用程序状态.

特别是我担心上面的选项2.极端情况下,产品表几乎是每个表一个表,在这里加载给定产品的应用程序状态需要从每个产品事件表加载该产品的所有事件.这桌爆炸对我来说有点不对劲.

我确信"这取决于",虽然没有单一的"正确答案",但我试图了解可接受的内容,以及完全不可接受的内容.我也知道NoSQL可以在这里提供帮助,其中事件可以存储在聚合根目录中,这意味着只有一个请求数据库来获取事件来重建对象,但是我们没有使用NoSQL数据库.那一刻,所以我在寻找替代品.



1> Dennis Traub..:

事件存储不需要知道事件的特定字段或属性.否则,模型的每次修改都将导致必须迁移数据库(就像在旧式的基于状态的持久性中一样).因此,我不会建议选项1和2.

下面是Ncqrs中使用的模式.如您所见,表"Events"将相关数据存储为CLOB(即JSON或XML).这对应于您的选项3(只有没有"ProductEvents"表,因为您只需要一个通用的"事件"表.在Ncqrs中,到您的聚合根的映射通过"EventSources"表发生,其中每个EventSource对应一个实际聚合根.)

Table Events:
    Id [uniqueidentifier] NOT NULL,
    TimeStamp [datetime] NOT NULL,

    Name [varchar](max) NOT NULL,
    Version [varchar](max) NOT NULL,

    EventSourceId [uniqueidentifier] NOT NULL,
    Sequence [bigint], 

    Data [nvarchar](max) NOT NULL

Table EventSources:
    Id [uniqueidentifier] NOT NULL, 
    Type [nvarchar](255) NOT NULL, 
    Version [int] NOT NULL

Jonathan Oliver的Event Store实现的SQL持久性机制基本上由一个名为"Commits"的表组成,其中BLOB字段为"Payload".这与Ncqrs几乎相同,只是它以二进制格式序列化事件的属性(例如,添加加密支持).

Greg Young建议采用类似的方法,如Greg网站上广泛记录的那样.

他典型的"事件"表的架构如下:

Table Events
    AggregateId [Guid],
    Data [Blob],
    SequenceNumber [Long],
    Version [Int]


@theBoringCoder听起来你有事件采购和CQRS混淆或者至少在你脑海中捣乱.他们经常被发现在一起,但他们不是一回事.CQRS让您将读写模型分开,而Event Sourcing让您使用事件流作为应用程序中的单一事实来源.
@MarijnHuizendveld你可能不想查询事件存储本身.最常见的解决方案是连接几个将事件投影到报告或BI数据库的事件处理程序.重播这些处理程序的事件历史记录.
很好的答案!我一直在阅读有关使用EventSourcing的主要观点之一是查询历史记录的能力.当所有有趣的数据被序列化为XML或JSON时,我将如何制作一个高效的查询工具?是否有任何有趣的文章寻找基于表格的解决方案?

2> cdmdotnet..:

GitHub项目CQRS.NET有一些具体的例子,介绍如何使用几种不同的技术进行EventStore.在编写本文时,SQL中有一个使用Linq2SQL和SQL模式的实现,其中一个用于MongoDB,一个用于DocumentDB(CosmosDB,如果你在Azure中),一个使用EventStore(如上所述).Azure中的更多内容,如表存储和Blob存储,与平面文件存储非常相似.

我想这里的要点是它们都符合相同的委托人/合同.它们都将信息存储在一个地方/容器/表中,它们使用元数据来识别来自另一个事件的一个事件,并且"只是"按原样存储整个事件 - 在某些情况下序列化,支持技术,就像它一样.因此,根据您选择文档数据库,关系数据库甚至平面文件,有几种不同的方法可以达到事件存储的相同意图(如果您在任何时候改变主意并发现需要迁移或支持,这将非常有用不止一种存储技术).

作为项目的开发人员,我可以分享一些关于我们做出的选择的见解.

首先我们发现(即使使用唯一的UUID/GUID而不是整数)由于多种原因出于战略原因而出现顺序ID,因此只有ID对于密钥而言不够独特,因此我们将主ID密钥列与数据/合并对象类型创建应该是真正的(在您的应用程序意义上)唯一键.我知道有些人说你不需要存储它,但这取决于你是否是绿地还是必须与现有系统共存.

出于可维护性的原因,我们坚持使用单个容器/表/集合,但我们确实为每个实体/对象使用了一个单独的表.我们在实践中发现,这意味着应用程序需要"创建"权限(通常说这不是一个好主意...通常,总是有例外/排除)或每次新实体/对象出现或部署时,新需要储存容器/桌子/收藏品.我们发现这对于本地开发来说非常缓慢,并且对于生产部署来说也是有问题的.你可能没有,但那是我们的真实体验.

要记住的另一件事是,要求操作X发生可能会导致发生许多不同的事件,从而了解命令/事件/有用的所有事件.它们也可以跨越不同的对象类型,例如,在购物车中推送"购买"可以触发帐户和仓库事件以触发.消费应用程序可能想知道所有这些,因此我们添加了CorrelationId.这意味着消费者可以询问因其请求而提出的所有事件.你会在架构中看到它.

特别是使用SQL,我们发现如果索引和分区没有得到充分利用,性能确实成了瓶颈.如果使用快照,请记住,事件需要以相反的顺序进行流式处理.我们尝试了一些不同的索引,发现在实践中,需要一些额外的索引来调试生产中的实际应用程序.您将再次在架构中看到它.

其他生产中的元数据在基于生产的调查中很有用,时间戳让我们深入了解事件持续与提升的顺序.这为我们提供了一些特别重要的事件驱动系统的帮助,该系统引发了大量事件,为我们提供了有关网络和网络系统分布等性能的信息.

推荐阅读
凹凸曼00威威_694
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有