基于事件驱动架构构建微服务第9部分:处理更新

原文链接:https://logcorner.com/building-microservices-through-event-driven-architecture-part10-handling-updates-and-deletes/

在本文中,我将讨论如何处理事件溯源系统上的更新。

在前面的步骤中,我将系统的所有业务变化存储为事件,而不是存储当前状态。我通过将所有事件应用于聚合来重建当前状态。

我已经建立了一个领域事件列表:过去发生的业务变化以通用语言表达:例如ThePackageHasBeenDeliveredToCustomer。

领域事件是不可变的,当事件发生时它不能改变。

因此,要纠正事件中的错误,我必须创建一个具有正确值的补偿事件,例如银行帐户交易。

聚合记录已提交的事件并保护业务不变量。这是事务边界。为了处理并发,我将使用带有版本控制的乐观并发控制 (OCC)。

在不获取锁的情况下,每个事务都会验证没有其他事务修改了它所读取的数据。如果数据没有改变,则提交事务,如果数据被其他人改变,则事务回滚并可以重新启动。

使用版本控制,用户读取聚合的当前状态,然后发送带有版本号的命令,如果版本号与聚合的当前版本匹配,则提交事务。

如果版本号与聚合的当前版本不匹配,在这种情况下,这意味着数据已被其他人更新。所以用户应该再次读取数据以获得正确的版本并重试。

在本教程中,我将展示如何更新语音实体。它具有以下属性:标题、描述、网址和类型。所以每个属性的更新都是一个事件,应该存储在事件存储中。

处理领域模型的更新

处理更新标题

测试用例1:当title为null或为空时,ChangeTitle应引发ArgumentNullAggregateException:

在这里,我将测试如果Title为NullOrEmpty,则系统应该引发异常。

08f8359b4518e90a5271457504c06e06.png

测试用例2:当预期版本不等于聚合版本时的ChangeTitle应该引发ConcurrencyException:

在这里我将测试如果预期版本不等于聚合版本,则系统应该引发异常。

因为我创建了一个新语音,聚合版本等于0,所以如果我将expectedVersion设置为1,测试应该会引发异常。

5018e13b68cdd17d3f82a7982ff212ff.png

测试用例3:具有有效参数的ChangeTitle应应用SpeechTitleChangedEvent:

在这里我将测试,如果没有错误,则应将newTitle应用于演讲的标题。换句话说:Speech.Title = “更新后新标题的值”

由于Apply函数将事件应用于聚合,因此语音的标题应等于SpeechTitleChangedEvent值的标题。

7685e13ef0dd8b364a7c153dd3194001.png

ChangeTitle最终实现:

ChangeTitle的最终实现应该是这样的。

很简单,我的标题不为空或为空,应用SpeechTitleChangedEvent。apply函数使用事件SpeechTitleChangedEvent的值设置演讲标题。

检查聚合版本的代码是在前面的步骤中开发的(参见aggregateroot.cs类)

public void ValidateVersion(long expectedVersion)
{
if (Version != expectedVersion)
{
throw new ConcurrencyException($@”Invalid version specified : expectedVersion = {Version}  but originalVersion =   {expectedVersion}.”);
}
}

52a44922dbc457b891be772b0439580e.png

处理更新DESCRIPTION、URL和TYPE

ChangeDescription、ChangeUrl和ChangeType应遵循与ChangeTitle相同的场景

处理申请更新

处理更新标题

测试用例1:当Command为空时处理更新应该引发ApplicationArgumentNullException :

在这里我将测试如果updateCommand为空,那么系统应该引发异常。

所以我应该模拟所有外部依赖项:IUnitOfWork、ISpeechRepository和IEventSourcingSubscriber

我将提供一个空命令并验证是否引发了ApplicationArgumentNullException。

bd0aaa3674772c1c3296a3b5ae0cb0c9.png

测试用例2:当语音不存在时处理更新应该引发ApplicationNotFoundException:

这里我将测试如果要更新的语音不存在,那么系统应该引发异常(ApplicationNotFoundException)。

我必须安排我的存储库,以便它返回带有模拟的空语音:

moqEventStoreRepository.Setup(m => m.GetByIdAsync<Domain.SpeechAggregate.Speech>(command.SpeechId))
.Returns(Task.FromResult((Domain.SpeechAggregate.Speech)null));

就像这样。

5c3b17e687c5e61c70d8882659b2ae1f.png

测试用例3:当命令不为空时处理更新应更新语音标题:

这里我测试一下,如果命令不为空,并且数据库中存在要更新的语音,则应该更新标题。

验证语音标题是否被修改的一种方法是在将其发送到存储库之前检查它的值,它应该等于新标题的值:

moqSpeechRepository.Verify(m =>
m.UpdateAsync(It.Is<Domain.SpeechAggregate.Speech>(n =>
n.Title.Value.Equals(command.Title)
)),Times.Once);

fff11de20820e89fc6c59284106f429a.png

测试用例4:当预期版本不等于聚合版本时处理更新应该引发ConcurrencyException:

在这里我将测试如果预期版本不等于聚合版本,那么系统应该引发异常。

聚合等于零,因为我实例化了一个新的语音,然后如果expectedversion不等于零,则系统应该引发ConcurrencyException。

87b7a520d64e8266ccbe5120573d1f7e.png

处理仓储更新

处理更新

测试用例1:当Speech为空时处理更新应该引发RepositoryArgumentNullException :

c00ebec993193b5fb4fcee64b6339b64.png

测试用例2:当语音不存在时处理更新应该引发NotFoundRepositoryException

62c557620a2afa7e33db1e8b6cdc6524.png

测试用例3:当语音有效且存在时处理更新应执行更新

325a871b1e4bf5c1eaa9580c304198e3.png

以及最终的实现

505610c69b721fb6eca454ede0ee758a.png

处理PRESENTATION的更新

处理更新

测试用例1:当ModelState无效时更新语音应返回BadRequest:

5e596ee225ead6c808b9d801f66921df.png

测试用例2:发生异常时的UpdateSpeech应引发InternalServerError

同上注册语音(ExceptionMiddleware)

测试用例3:当ModelState有效且没有错误时更新语音应该返回Ok

7be786b3b2d34171d07d0c42f91946e9.png

以及最终的实现

189eb1ad6d38c59a414d4bd4c3040f4e.png9714105f94f7248e55f76ee8836654c4.png

用POSTMAN测试

按F5并启动postman和sql server。

让我们启动sql server看看发生了什么 让我们运行一个select查询,你可以看到[dbo].[Speech]和[dbo].[EventStore]这两个表是空的。

60329c5a5b0f67d1ff755b391fc1be42.png

让我们启动postman并运行一个post请求来创建一个演讲:http://localhost:62694/api/speech

postman脚本在这里:LogCorner.EduSync.Command\src\Postman\BLOG.postman_collection.json

c8518fc461ca6e04d51dec069ccbc664.png

现在我应该有一个新创建的演讲和一个事件LogCorner.EduSync.Speech.Domain.Events.SpeechCreatedEvent, LogCorner.EduSync.Speech.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 请注意,版本等于 0。对于每个新语音,版本应为零。

如果我检查有效载荷,我必须看到我的事件

a1b6d49e0d510208b3793a91d2cb3e1c.png

{
“Title”: {
“Value”: “Le Lorem Ipsum est simplement du faux texte”
},
“Url”: {
“Value”: “http://www.yahoo_1.fr”
},
“Description”: {
“Value”: “Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l’imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte”
},
“Type”: {
“Value”: 3
},
“AggregateId”: “7c8ea8a0-1900-4616-9739-7cb008d37f74”,
“EventId”: “a688cc8a-ed56-4662-bbad-81e66ed917a0”,
“AggregateVersion”: 0,
“OcurrendOn”: “2020-01-19T15:49:59.3913833Z”
}

为了更新演讲的标题,我运行以下请求 http://localhost:62694/api/speech 这是一个PUT请求。

我拿到了新建语音的标识符CF17D255-9991-4B7B-B08E-F65B54AA9335 让我们从sql复制并将其粘贴到请求正文中。

好的,现在我可以运行put查询

e5d05fc7ba23811d1c71c337e5e3f16a.png

回到sql server验证结果

SELECT * FROM [dbo].[Speech]

SELECT * FROM [dbo].[EventStore]

我应该看到更新的标题和一个新事件LogCorner.EduSync.Speech.Domain.Events.SpeechTitleChangedEvent。

版本应为 1,有效负载应为更新事件

{
“Title”: “UPDATE_1__Le Lorem Ipsum est simplement du faux texte”,
“AggregateId”: “7c8ea8a0-1900-4616-9739-7cb008d37f74”,
“EventId”: “de253f69-ea89-4a54-8927-e09553cc43c7”,
“AggregateVersion”: 1,
“OcurrendOn”: “2020-01-19T15:55:14.1734365Z”
}

bca1baba17e2eb3028a7ed9e169baa49.png

本文的源代码可在此处获得 (Feature/Task/EventSourcingApplication)

https://github.com/logcorner/LogCorner.EduSync.Speech.Command/tree/Feature/EventSourcingHandlingUpdates

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/296244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

理解离散傅立叶变换(一. 傅立叶变换的由来)

转自&#xff1a;http://blog.csdn.net/dznlong/article/details/2261150理解离散傅立叶变换&#xff08;一&#xff09;------傅立叶变换的由来关于傅立叶变换&#xff0c;无论是书本还是在网上可以很容易找到关于傅立叶变换的描述&#xff0c;但是大都是些故弄玄虚的文章&…

浅谈入行

2019独角兽企业重金招聘Python工程师标准>>> 2006年开始参加工作&#xff0c;从编码&#xff0c;到带人&#xff0c;再到设计&#xff0c;自己不知不觉也走过了许多个年头。在公司从外包&#xff0c;到国内前后也参加了十多个不同的项目&#xff0c;每年都会有一些新…

数学界再出变态神人!竟用一个比基尼方程,暴力吊打美国数学家!看完我惊了......

全世界只有3.14 % 的人关注了爆炸吧知识没时间了快上车&#xff01;最近&#xff0c;有粉丝给超模君发了一些图片。早跟你们要说&#xff0c;要好好学数学。现在好了&#xff0c;连沙发问题都不懂&#xff01;没办法&#xff0c;作为数学界屈指可数的老司机&#xff0c;是时候挺…

Envoy实现.NET架构的网关(四)集成IdentityServer4实现OAuth2认证

.NET网关与Gateway实战-Envoy与kong课程什么是OAuth2认证简单说&#xff0c;OAuth 就是一种授权机制。数据的所有者告诉系统&#xff0c;同意授权第三方应用进入系统&#xff0c;获取这些数据。系统从而产生一个短期的进入令牌&#xff08;token&#xff09;&#xff0c;用来代…

java中transferto_被朋友问到什么是零拷贝,我一脸懵逼…

前言我们的Web应用多多少少都会处理一些静态内容&#xff0c;需要先从磁盘中读取到数据&#xff0c;在不经过修改后将此数据写入到套接字&#xff0c;伪代码如下&#xff1a;read(file, tmp_buf, len);write(socket, tmp_buf, len);虽然看似简单&#xff0c;但是它的效率却不高…

20个天才般的走心设计,真是太牛了!

全世界只有3.14 % 的人关注了爆炸吧知识科技发展一日千里&#xff0c;每天都有很多实用的小物被发明出来。下面就是20个超聪明的日常小发明&#xff0c;看过后你一定也想拥有&#xff01;“页面”椅子&#xff0c;可以帮助用户调整座椅高度&#xff0c;灵感来自于书籍装上这个以…

Android拨号盘,支持T9搜索和号码搜索

之前做通讯录软件&#xff0c;其中在做拨号盘的时候一直为怎么实现T9输入烦恼&#xff0c;上网找了很多帖子&#xff0c;都没有满意的答案。不过最后终于是实现了&#xff0c;看社区内好像也有不少朋友需要&#xff0c;在此分享一下。这个是在我项目中提取出来的拨号盘案例&…

浅谈.Net异步编程的前世今生----异步函数篇(完结)

前言上一篇我们着重讲解了TPL任务并行库&#xff0c;可以看出TPL已经很符合现代API的特性&#xff1a;简洁易用。但它的不足之处在于&#xff0c;使用者难以理解程序的实际执行顺序。为了解决这些问题&#xff0c;在C# 5.0中&#xff0c;引入了新的语言特性&#xff0c;被称为异…

NSInteger,NSUInteger,NSNumber

2019独角兽企业重金招聘Python工程师标准>>> Objective-C入门教程10:数字类型&#xff08;NSInteger,NSUInteger,NSNumber&#xff09; 柳志超博客 Program Objective-C Objective-C入门教程10:数字类型&#xff08;NSInteger,NSUInteger,NSNumber&#xff09; p…

听说麦当劳,买一个雪糕就送一个男友!

1 麦当劳买雪糕免费送男友&#xff01;▼2 当90后成了家长......▼3 不要跟有鼻子的人握手可能刚刚扣过鼻屎▼4 鸟&#xff1a;别瞎玩&#xff01;快开车&#xff01;▼5 凭实力当上群主&#xff01;▼6 要是有喜欢的女生千万不要问她闺蜜的意见▼7 终于&#xff0c;我们…

windows server 2008更新补丁失败排错

首先描述故障故障&#xff1a;1&#xff0c;windows服务器上丢失了共享磁盘。2&#xff0c;打开服务器管理器报错3&#xff0c;更新补丁报错&#xff0c;错误代码&#xff1a;800B01004&#xff0c;手动安装.net 3.5安装包同样报错话说这次出差帮客户解决问题。遇到了这样一个错…

WPF实现统计图

WPF开发者QQ群&#xff1a; 340500857 | 微信群 -> 进入公众号主页 加入组织有小伙伴提出需要实现统计图。 由于在WPF中没有现成的统计图控件&#xff0c;所以我们自己实现一个。PS&#xff1a;有更好的方式欢迎推荐。01—代码如下一、创建 Basi…

手绘图解:从零维到十维空间

全世界只有3.14 % 的人关注了爆炸吧知识事情是这样的&#xff0c;这周我给学生讲3dmax的课。为了让学生了解三视图我就顺便科普了一下什么是零维、一维、二维、三维空间。讲完不过瘾&#xff0c;感觉一支粉笔一块黑板讲维度是一件很爽的事情&#xff0c;那么.........接下来请同…

ISA server的常见身份验证方式

ISA 2006的几种常用验证方式&#xff1a;1. 基本验证&#xff1a;此验证方式不会被加密&#xff0c;只是以明文的方式来传递信息&#xff0c;不安全。如果在“网络”的“内部”属性中将“域”选项卡里边的“选择域”来配置默认域&#xff0c;那么就会把用户送来的帐户与密码信息…

字节前端终于开源!吹爆!

Semi Design 发布&#xff0c;前端同学的福音大家好&#xff0c;我是鱼皮。最近&#xff0c;字节跳动的抖音前端技术团队开源了一款企业级应用设计系统 Semi Design 。这也是他们团队在 GitHub 上首次公开的项目&#xff0c;短短几天&#xff0c;就收获了 3.6 k 个 star。GitH…

CSS2-3常见的demo列子总结

CSS2-3常见的demo列子总结 阅读目录 1. css超过一行或者多行后显示省略号。2. css图片未知高度垂直居中完美解决方案。3. 学习使用 :before和 :after伪元素回到顶部1. css超过一行或者多行后显示省略号。 Css实现超过一行后显示省略号&#xff1b;代码如下&#xff1a;<p st…

18张难以置信的照片,封面这张你就没见过

全世界只有3.14 % 的人关注了爆炸吧知识感谢网络&#xff0c;只要点几下鼠标&#xff0c;就能看到我们以前从未见过的东西——有些甚至是难以置信的&#xff01;鲸鱼的心脏水中的鲨鱼卵幼年的箭鱼萌萌哒世界上最高的棕榈树&#xff0c;简直以为是PS的没见过的话&#xff0c;很容…

.NET 生态系统的蜕变之 .NET 6

.NET 6 是自.NET 4 框架以来生态系统看到的最大版本更新&#xff0c;虽然.NET Core 是2014年开始非常大的一项重大战略举措&#xff0c;但是.NET 6是真正的具有强大动力的非常重要的版本。2021年11月9日即将正式发布的.NET 6, 也许你认为.NET 5才刚刚发布&#xff0c;我才刚开始…

我看你还能坚持多久?!

1 我看你还能坚持多久&#xff01;▼2 依旧是熟悉的配方▼3 到哪儿都不愁工作......▼4 请问&#xff0c;当事喵作何感想&#xff1f;▼5 池塘危险&#xff0c;请勿靠近&#xff01;&#xff08;图源网络&#xff0c;侵删&#xff09;▼6 望周知&#xff01;▼7 实在是无…

Hello Blazor:(13)查找HTML元素对应.razor文件

前言Blazor是基于组件的开发&#xff0c;每个组件都是以一个.razor文件形式存在。当应用程序变得越来越大并且.razor文件的数量和层次结构越来越多时&#xff0c;想很快弄清页面上的HTML元素是由哪个组件生成的&#xff0c;就变得不那么容易了&#xff01;FindRazorSourceFile介…