通知事件的设计

时间:2015-10-21 08:45:22

标签: events event-sourcing

我正在设计一些事件,这些事件将在执行操作或系统中的数据更改时引发。这些事件可能会被许多不同的服务所消耗,并将被序列化为XML,但更广泛地说,我的问题也适用于设计更现代的时髦事物,如Webhooks。

我专门考虑如何用事件描述更改,并且难以在不同的实现之间进行选择。让我来说明我的问题。

想象一下,创建了一个客户,并引发了一个简单的事件。

<CustomerCreated>
  <CustomerId>1234</CustomerId>
  <FullName>Bob</FullName>
  <AccountLevel>Silver</AccountLevel>
</CustomerCreated>

现在让我们说鲍勃花了很多钱并成为黄金客户,或者确实改变了任何其他财产(例如:他现在更喜欢被称为罗伯特)。我可以举办这样的活动。

<CustomerModified>
  <CustomerId>1234</CustomerId>
  <FullName>Bob</FullName>
  <AccountLevel>Gold</AccountLevel>
</CustomerModified>

这很好,因为Created和Modified事件的模式是相同的,并且任何订阅者都接收实体的完整当前状态。然而,任何接收器都很难确定哪些属性已经改变而没有跟踪状态本身。

然后我想到了这样的事件。

<CustomerModified>
  <CustomerId>1234</CustomerId>
  <AccountLevel>Gold</AccountLevel>
</CustomerModified>

这是更紧凑的,只包含已更改的属性,但附带的缺点是接收方必须应用更改并在需要时重新组合实体的当前状态。此外,Created和Modified事件的模式现在必须不同; CustomerId是必需的,但所有其他属性都是可选的。

然后我想出了这个。

<CustomerModified>
  <CustomerId>1234</CustomerId>
  <Before>
    <FullName>Bob</FullName>
    <AccountLevel>Silver</AccountLevel>
  </Before>
  <After>
    <FullName>Bob</FullName>
    <AccountLevel>Gold</AccountLevel>
  </After>
</CustomerModified>

这涵盖了所有基础,因为它包含完整的当前状态,加上接收器可以找出已经改变的内容。 BeforeAfter元素与Created事件具有完全相同的模式类型。但是,它非常冗长。

我很难找到任何好事例;我还应该考虑其他任何模式吗?

3 个答案:

答案 0 :(得分:2)

您将问题标记为&#34;事件采购&#34;,但您的问题似乎更多是关于事件驱动的SOA。

我同意@Matt的回答 - &#34; CustomerModified&#34;如果有多个业务原因导致客户更改,则不足以捕获意图。

但是,我会更进一步备份并要求您考虑为什么将客户信息存储在本地服务中,而您(可能)已经为客户提供了真相来源。消费客户信息的起点应该是在需要时从源头获取。存储可以从源中可靠查询的信息副本很可能是不必要的优化(和复杂化)。

即使您确实需要在本地存储客户数据(并且确实有正当理由需要这样做),请考虑仅传递构建查询真相来源所必需的数据(发出事件的服务):

<SomeInterestingCustomerStateChange>
  <CustomerId>1234</CustomerId>
</SomeInterestingCustomerStateChange>

因此,这些事件类型可以根据需要进行细化,例如&#34; CustomerAddressChanged&#34;或简单地&#34; CustomerChanged&#34;,由消费者根据事件类型查询所需的信息。

没有&#34;一刀切的&#34;解决方案 - 有时使用事件传递相关数据会更有意义。同样,如果这是你需要进入的方向,我同意@Matt的答案。

根据评论进行修改

我同意使用ESB查询通常不是一个好主意。有些人以这种方式使用ESB,但恕我直言,这是一种不好的做法。

您的原始问题和您对此答案的评论以及Matt的讨论仅涉及已更改的字段。这在许多语言中肯定会出现问题,您必须以某种方式区分属性为空/ null和未包含在事件中的属性。如果事件从/向静态类型序列化/反序列化,那么知道&#34; First Name被设置为NULL&#34;之间的区别将是痛苦的(如果不是不可能的话)。并且&#34;名字丢失,因为它没有改变&#34;。

根据您对系统同步的评论,我的建议是在每次更改时发送完整的数据集(假设信号+查询不是一个选项)。这使得数据的解释直到每个消费系统,并且限制了发布者发布更通用事件的责任,即&#34;客户1234已被修改为X状态&#34;。此事件似乎比其他选项更有用,如果其他系统收到此事件,他们可以按照自己的意愿解释它。他们可以为客户1234转储/重写他们自己的数据,或者他们可以将其与他们拥有的数据进行比较,并仅更新更改的内容。仅发送更改的内容似乎更具体针对单个消费者或特定类型的消费者。

所有这一切,我都不认为您提出的任何解决方案都是&#34;对&#34;或&#34;错误&#34;。你最了解什么对你的独特情况有用。

答案 1 :(得分:1)

事件应该用于描述意图和细节,例如,您可以拥有一个CustomerRegistered事件,其中包含已注册客户的所有详细信息。然后在流中稍后发生CustomerMadeGoldAccount事件,该事件只需要捕获客户的客户ID已更改为黄金。

由事件的消费者来构建他们感兴趣的系统的当前状态。

这样只允许在每个事件中存储最相关的信息,假设客户拥有数百个属性,如果每个更改单个属性的命令都必须在之前和之后引发具有所有属性的事件,这将变得难以处理很快。如果您只是发布一个通用的CustomerModified事件,也很难确定更改发生的原因,这通常是一个询问实体当前状态的问题。

仅捕获与事件相关的数据意味着发出事件的命令只需要有足够的有关实体的数据来验证命令是否可以执行,它甚至不需要读取整个客户实体。

事件的订阅者也只需要为他们感兴趣的事物建立一个状态,例如:也许“帐户级”小部件正在监听这些事件,它需要保留的只是客户ID和帐户级别,以便它可以显示客户所在的帐户级别。

答案 2 :(得分:0)

而不是试图通过有效载荷xmls&#39;来传达一切。字段,你可以区分不同的操作 - 1.取决于操作的不同端点URL(这是首选) 2.将操作码(操作代码)作为xml文件中的一个元素,它告诉用于处理传入请求的操作。(更接近您的示例)

有一些适用于您的业务案例的企业模式 - 消息传递及其变体,如果您的系统是可扩展的,则应使用Enterprise Service Bus。 ESB允许可靠地处理事件和处理。