原文链接:https://logcorner.com/building-microservices-through-event-driven-architecture-part7-implementing-eventsourcing-on-repositories/
在本文中,我将讨论Repository上的Event Sourcing实现。
仓储负责将事件添加到事件存储并从事件存储中检索所有事件。
保存聚合后,所有与该聚合根相关的未提交事件都会添加到事件存储表中。
eventstore表是一个只能追加的表(不允许更新和删除)。
eventstore表的架构如下所示:
Id 主键
Version 聚合的版本
AggregateId 聚合的标识符
Name 事件的名称:2@735f8407-16be-44b5-be96-2bab582b5298
TypeName 事件的类型:LogCorner.EduSync.Speech.Domain.Events.Speech.SpeechCreatedEvent、LogCorner.EduSync.Speech.Domain、Version=1.0.0.0、Culture=neutral、PublicKeyToken=null
OccurredOn 事件日期
PayLoad 事件流:
{
“Title”: { “Value”: “Introducing Azure Cosmos DB” },
“Url”: { “Value”: “https://azure.microsoft.com/en-us/resomurcejjjnns/videos/azure-friday-introducing-azurkke-cosmos-db_g5/” },
“Description”: { “Value”: “Kirill Gavrylyuk stops by Azure Friday to talk Cosmos DB with Scott Hanselman. Watch quick overview of the industry’s first globally distributed multi-model database service followed by a demo of moving an existing MongoDB app to Cosmos DB with a single config change.” },
“Type”: { “Value”: 2 },
“AggregateId”: “735f8407-16be-44b5-be96-2bab582b5298”,
“EventId”: “6eb58cb4-da5e-46d4-8325-e742a20935ab”,
“AggregateVersion”: 0,
“OcurrendOn”: “2019-09-08T10:55:48.5528117Z”
}
IsSync 指示事件是否同步的布尔值
要重建聚合的当前状态,我们必须读取与给定聚合ID相关的所有事件,然后调用函数LoadFromHistory。此函数属于AggregateRoot类,应将所有事件应用于聚合。
事件溯源接口
让我们定义IEventStoreRepository接口
GetByIdAsync 一个函数,用于从事件存储中检索与聚合相关的所有事件。
AppendAsync 一个将事件附加到事件存储的函数
事件溯源实现
让我们定义EventStore类
Id 事件流的标识符
Version 当前的聚合版本
AggregateId 聚合的标识符
Name 事件流的名称
TypeName 事件的完整类型(例如:LogCorner.EduSync.Speech.Domain.Events.Speech.SpeechCreatedEvent、LogCorner.EduSync.Speech.Domain、Version=1.0.0.0、Culture=neutral、PublicKeyToken=null)
OccurredOn 事件发生的日期
SerializedBody 事件序列化的JSON
AppendAsync 一个函数,用于指示事件是否已同步。
APPENDASYNC 实现
测试用例1:AppendAsync应该在eventstore上附加一个事件:
在这里,我必须模拟一个上下文,创建一个 IEventStoreRepository实例并使用EventStore对象调用AppendAsync,然后上下文应该有一组带有单个元素的 EventStore,并且该元素应该等于我在AppendAsync的参数中传递的eventstore对象.
测试可能如下所示:
但是我将使用entityframeworkcore inmemory数据库而不是使用moq,这是一种更简单的数据库单元测试方法。
所以我可以像这样实例化一个内存上下文:
下一步是创建一个EventStoreRepository类
然后将DbSetEventStore属性添加到DatabaseContex类
代码编译成功但测试失败,因为DbSetEventStore为空。
所以让我们配置一些映射
EventStoreEntityTypeConfiguration类负责将EventStore类映射到EventStore数据库表
最终测试如下所示:
GETBYIDASYNC实现
GetByIdAsync从事件存储中检索与聚合相关的所有事件。
在这里,我必须创建一个Speech类型的空聚合,从与它相关的事件存储中读取所有事件,最后应用这些事件。
测试用例2:AggregateRoot的CreateInstance应该返回一个空的聚合
让我们创建一个Invoker类,负责创建一个空的聚合根
测试通过
测试用例3:GetByIdAsync和BadAggregateId应该引发BadAggregateIdException
给定一个错误的aggregateId(例如:空的aggregateId),GetByIdAsync应该引发一个异常(BadAggregateIdException)
测试用例4:GetByIdAsyncWithNullInstanceOfAggregateShouldRaiseNullInstanceOfAggregateIdException
给定不存在的聚合ID(例如:空聚合 ID),GetByIdAsync应该引发异常 (NullInstanceOfAggregateIdException)
测试用例5:GetByIdAsyncWithoutEventsShouldReturnEmptyList
如果没有与给定聚合ID相关的事件,则GetByIdAsync应返回一个空列表
测试用例6:GetByIdAsyncWithEventsShouldReturnTheCurrentStateOfAggregate
在这里,我必须从事件存储中读取与给定聚合ID相关的所有事件,重建聚合状态并返回它。
为了达到这个目标,我必须根据事件类型反序列化json结构中的事件。
测试用例6.1:反序列化事件流应该返回一个事件
在这里,我将创建一个将json字符串反序列化为Event对象的函数
测试用例6.2:GetByIdAsyncWithEventsShouldReturnTheCurrentStateOfTheAggregate
到这里,我将完成我的函数的实现
本文的源代码可在此处获得 (Feature/Task/EventSourcingRepository)
https://github.com/logcorner/LogCorner.EduSync.Speech.Command/tree/Feature/Task/EventSourcingRepository