事件采购与可删除数据相结合

时间:2017-10-12 14:35:13

标签: blob domain-driven-design

我想开发一个提供Emailer命令的SendEmail微服务。在微服务中,我有一个聚合物,它代表整个电子邮件过程,包含以下事件:

汇总Email

  • EmailCreated
  • EmailDeliveryStarted
  • EmailDeliveryFailed
  • EmailRecipientDelivered当其中一位收件人收到电子邮件时
  • EmailRecipientDeliveryFailed当其中一位收件人无法收到电子邮件时

在后台使用电子邮件递送服务SendGrid;我的微服务就像我自己的事件一样。来自SendGrid的传入webhook被转换为正确的域事件。

这个过程看起来像这样:

  1. 命令SendEmail ==> EmailCreated

  2. EmailCreatedHandler ==>电子邮件。Send(发送给网格)

  3. 传入的webhook ==> EmailDeliveryStarted

  4. 进一步的webhooks ==> EmailRecipientDeliveredEmailRecipientDeliveryFailed
  5. 当然,如果我想要更换外部网络服务并且它会应用其他消息传递策略,我会适应这一点,但保留我的域模型与它的事件。我想让客户不要担心具体的电子邮件传送策略。

    现在我面临的关键问题是:我想接受SendEmail命令,即使SendGrid当时不可用,这需要存储整个电子邮件数据(带附件),然后使用事件处理程序,开始发送过程。另一方面,我不想用这个BLOB数据膨胀我的初始EmailCreated事件。我希望能够在SendGrid接受我的发送电子邮件请求后清理这些数据。

    我也可以尝试将电子邮件发送到SendGrid并在EmailDeliveryStarted命令中存储初始SendEmail事件。但这感觉就像一个两阶段的提交:如果SendGrid接受了我的调用,但不知何故我的存储库无法存储EmailDeliveryStarted事件,客户端将被告知出现了问题并再次尝试这将是一场灾难。 / p>

    所以我不知道如何设计我的聚合,更重要的是,我的EmailCreated事件,因为它不应该包含附件等BLOB数据。

1 个答案:

答案 0 :(得分:0)

我觉得这个问题很有意思,需要一点点反思。

首先要做的事情 - 我认为没有义务在活动中存储电子邮件附件。您只需存储附加文件的完全限定名称即可。这样可以保持事件日志更小,并可能排除"删除"事件(你知道,在事件源模型中,你不应该这样做。)

其次,假设项目没有构建电子邮件客户端,我不认为需要将电子邮件建模为聚合根。我看到AggregateRoots代表业务相关的域,而不是像发送电子邮件这样的实用程序任务。您可以使用数据库表/文档更轻松地对此进行建模,该数据库表/文档可以跟踪已发送的内容和尚未发送的内容。我看到通过SendGrid发送电子邮件作为对业务事件的反应,当然要跟踪,但本身不是AggregateRoot。

最后,如果您希望在SendGrid脱机时也接受SendEmail命令,则聚合会发出EmailQueued事件。 EmailQueuedHandler将在负责进程的读取模型上生成一条线,将所有电子邮件置于排队状态并批量发送。如果与SendGrid的通信失败,您可以:

  • 什么都不做,发件人流程会在下次尝试时选择电子邮件
  • 发送一个EmailSendFailed,由Handler拦截,这将增加重试次数(如果您想在多次重试后停止)。

希望您的项目足够清晰且好运。