我最近一直在研究事件源,并对与客户的互动有一些疑问。
因此,事件源听起来很棒。解耦所有微服务,将信息保持在不可变的事件中,并根据这些信息制定存储状态来满足您的需求非常方便。让事件在您的系统/服务中传播并以自己的方式对事件做出反应就可以了。
我遇到的问题在于了解客户互动。
因此,您希望客户端与系统进行交互,但是他们现在需要通过事件来执行此操作。他们无法再提交一种状态来更改您现有的状态。
所以问题是客户如何触发特定事件并与之交互(不仅是基于事件的系统),还是与基于事件源的系统进行交互。
我的理解是,您不再将其余api用作资源(可以获取,更新,删除等。将它们作为资源来处理),而是将其作为事件发布到端点。
那么这些端点如何工作?
我的第二个问题是用户如何获得回复? 例如,假设我们有一个下订单的事件。 您将要触发的事件及其将要做的事情。同样,我的理解是您现在不验证请求,例如检查订购该订单的用户是否有足够的钱,而是将其放火并在系统中处理。 例如它不会 - 订单已下 -这将由定价服务收取,并会根据用户是否负担得起,触发预留金或超出金额事件。 -订单服务将监听这些订单,然后将订单标记为已拒绝或信用额不足。
因此,由于这是一个异步过程,并且用户已被解雇并被遗忘,因此您如何向用户显示其失败还是成功?您是否向他们显示一个订单确认页面,其中包含订单状态(即使其待处理) 还是您轮询它直到它改变(网络套接字等)。
很抱歉,如果其中很多都是废话,我仍在学习这种体系结构,并且非常重视具有REST响应的整体模式。
任何帮助将不胜感激。
答案 0 :(得分:0)
我遇到的问题在于了解客户互动。
有些问题可能是理解上的,但是我向您保证,这个问题中有相当一部分是文献很烂。
尤其是,“事件”一词被以许多不同的方式重复使用。如果您不十分注意正在使用的含义,那么您将被打结。
事件源实际上是关于持久性的-微型服务器如何存储其状态的私有副本以供以后重用?我们不会破坏性地覆盖以前的状态,而是编写链接回到先前状态的新信息。如果您想象每个微服务将每个状态更改作为提交存储在其自己的git存储库中,那么您就在正确的位置。
与使用事件消息在一个微服务和另一个微服务之间传递信息的动物不同。
当然存在一些明显的重叠,因为您可能会与其他微服务共享的一条消息是“我刚刚更改了状态”。
那么这些端点如何工作?
与Web表单相同。我向您发送表单的表示形式,客户端向您显示该表单。您填写数据并提交表单,客户端处理表单的内容,并向我发送HTTP请求,该请求在消息正文中带有“ FormSubmitted”事件。
您可以通过发送状态的新表示来获得类似的结果,但是它有点错误,容易剥夺语义意图,然后尝试在服务器上再次进行猜测。因此,您更有可能看到基于任务的用户界面或可以清晰识别更改语义的协议。
当外部世界是某些数据的授权机构(例如,购物者的送货地址)时,您更有可能看到更传统的“仅编辑现有表示形式”方法。
因此,由于这是一个异步过程,并且用户已被解雇并被遗忘,因此您如何向用户显示其失败还是成功?
Fire and forget
确实不适用于不可靠网络上的分布式协议。在大多数情况下,最少一次交付很重要,因此Fire until verified
是更常见的选择。最初对消息的确认可能类似于202 Accepted -“我们收到了您的消息,我们将其写下来,这是我们当前的进度,下面是一些您可以获取进度报告的链接”。
在我看来,事件源不适合传统的REST模型,您可以在其中挖掘资源。
Jim Webber's 2011谈话可能有助于消除噪音。 REST API是您的域模型使用的一种伪装。您交换有关操纵资源的消息,并且副作用是您的域模型可以完成有用的工作。
一种看起来更“传统”的方法是使用事件流的表示形式。我执行了GET /08ff2ec9-a9ad-4be2-9793-18e232dbe615
,它返回了事件列表的表示。我将一个新事件附加到该列表的末尾,然后PUT /08ff2ec9-a9ad-4be2-9793-18e232dbe615
发生了有趣的副作用。或者,也许我改为创建一个补丁文件来描述我的更改,并PATCH /08ff2ec9-a9ad-4be2-9793-18e232dbe615
。
但是更有可能,我会做其他事情-代替GET /08ff2ec9-a9ad-4be2-9793-18e232dbe615
获取事件列表的表示,我可能会GET /08ff2ec9-a9ad-4be2-9793-18e232dbe615
获取可用协议的表示-比方说,一个充满超链接的文档。从那里,我可能GET /08ff2ec9-a9ad-4be2-9793-18e232dbe615/603766ac-92af-47f3-8265-16f003ce5a09
获得数据收集表单的表示。我填写了活动的详细信息,提交了表单,并将POST /08ff2ec9-a9ad-4be2-9793-18e232dbe615
表单数据发送到服务器。
您当然可以在URI中使用任何喜欢的拼写。
在第一种情况下,我们需要类似HTTP的文档编辑器;第二种情况使用的更像是网络浏览器。
如果有很多不同类型的事件,那么第二种情况很可能具有很多不同的表单资源,都提交了POST /08ff2ec9-a9ad-4be2-9793-18e232dbe615
请求。
(您没有将所有表单提交到相同的URI,但是有advantages to consider)。
在非事件来源模式下,我想首先将其放入数据库中,然后引发事件。
即使您不是事件采购商,在发出事件之前将事件提交到耐用性商店也可能会有一些好处。参见Pat Helland:Data on the Outside versus Data on the Inside。
答案 1 :(得分:0)
因此,您希望客户端与系统进行交互,但是他们现在需要通过事件来进行此操作。
客户不必这样做。客户甚至可能不知道基础事件存储。
在实现基于事件的系统时,需要权衡一些考虑和做出决定。首先,您可以尝试列举一些计算机时代以前的事件源系统示例,并研究它们的非功能性特征。
所以问题是客户如何触发特定事件
客户端不发送事件。他们宁可表达意图(命令)。然后,事件源系统负责验证意图并拒绝它,或者接受并存储相应的事件。这意味着接受更改系统状态的意图,并且存储的事件确认更改。
我的理解是,您不再将其余api用作资源 REST是一种选择。您只是将不同的事物视为资源。命令可以是REST资源。源于事件的实体可以是您向其发布命令的资源。如果您希望它异步-您可以稍后获取该命令以检查其状态。您可以获取实体以了解其当前状态。您不能从一类实体中获取GET事件作为订阅方式。
如果我们谈论的是最终用户,那么很可能它不会直接处理事件存储。中间有一些第三层,即CQRS。从用户客户端的角度来看,可以为它提供REST,GraphQL,SOAP,gRPC或事件电子邮件。无论哪种运输解决方案,您都认为合适。 CQRS的命令处理部分是专门由域驱动的。它决定接受和拒绝的意图。
事件存储本身负责数据的一致性。即它不应允许两个导致无效状态的并发事件被发布。这是计算机前事件源系统所擅长的。通常,您会将某些物理对象作为一个实体,因此只需获得它即可锁定更新。
然后,最终用户客户端通常从某种准备好的读取模型读取数据。读取(CQRS中的R)组件的职责是为客户端准备读取优化的数据。此数据可能来自相同或不同类的多个事件源。同样,客户端可以通过任何合适的传输方式与读取模型进行交互。
虽然事件存储是一致且立即一致的,但读取模型最终还是一致的。但是由您自己来调整这种可能性。
只需尝试将REST从体系结构中抛弃一段时间。将其视为可用的传输选项之一-可能有助于查看根。