我想开发一个提供Emailer
命令的SendEmail
微服务。在微服务中,我有一个聚合物,它代表整个电子邮件过程,包含以下事件:
汇总Email
:
EmailCreated
)EmailDeliveryStarted
EmailDeliveryFailed
EmailRecipientDelivered
当其中一位收件人收到电子邮件时EmailRecipientDeliveryFailed
当其中一位收件人无法收到电子邮件时在后台使用电子邮件递送服务SendGrid;我的微服务就像我自己的事件一样。来自SendGrid的传入webhook被转换为正确的域事件。
这个过程看起来像这样:
命令SendEmail
==> EmailCreated
EmailCreatedHandler
==>电子邮件。Send
(发送给网格)
传入的webhook ==> EmailDeliveryStarted
EmailRecipientDelivered
,EmailRecipientDeliveryFailed
等当然,如果我想要更换外部网络服务并且它会应用其他消息传递策略,我会适应这一点,但保留我的域模型与它的事件。我想让客户不要担心具体的电子邮件传送策略。
现在我面临的关键问题是:我想接受SendEmail
命令,即使SendGrid当时不可用,这需要存储整个电子邮件数据(带附件),然后使用事件处理程序,开始发送过程。另一方面,我不想用这个BLOB数据膨胀我的初始EmailCreated
事件。我希望能够在SendGrid接受我的发送电子邮件请求后清理这些数据。
我也可以尝试将电子邮件发送到SendGrid并在EmailDeliveryStarted
命令中存储初始SendEmail
事件。但这感觉就像一个两阶段的提交:如果SendGrid接受了我的调用,但不知何故我的存储库无法存储EmailDeliveryStarted
事件,客户端将被告知出现了问题并再次尝试这将是一场灾难。 / p>
所以我不知道如何设计我的聚合,更重要的是,我的EmailCreated
事件,因为它不应该包含附件等BLOB数据。
答案 0 :(得分:0)
我觉得这个问题很有意思,需要一点点反思。
首先要做的事情 - 我认为没有义务在活动中存储电子邮件附件。您只需存储附加文件的完全限定名称即可。这样可以保持事件日志更小,并可能排除"删除"事件(你知道,在事件源模型中,你不应该这样做。)
其次,假设项目没有构建电子邮件客户端,我不认为需要将电子邮件建模为聚合根。我看到AggregateRoots代表业务相关的域,而不是像发送电子邮件这样的实用程序任务。您可以使用数据库表/文档更轻松地对此进行建模,该数据库表/文档可以跟踪已发送的内容和尚未发送的内容。我看到通过SendGrid发送电子邮件作为对业务事件的反应,当然要跟踪,但本身不是AggregateRoot。
最后,如果您希望在SendGrid脱机时也接受SendEmail
命令,则聚合会发出EmailQueued
事件。 EmailQueuedHandler
将在负责进程的读取模型上生成一条线,将所有电子邮件置于排队状态并批量发送。如果与SendGrid的通信失败,您可以:
EmailSendFailed
,由Handler
拦截,这将增加重试次数(如果您想在多次重试后停止)。希望您的项目足够清晰且好运。