我们正在使用事件来源并根据事件流构建聚合。 我有2个聚合 - A1和A2。 A1用作模板以创建A2。 A1的大小可能相当大。 事件源的基本思想是确保在事件对象中捕获对应用程序状态的每个更改。 因此,为了节省A2,我们必须在第一个事件中存储大量信息。
这种情况是常见的还是从模板创建并不是一个好主意? 有没有更好的方法来解决它?
答案 0 :(得分:3)
如果您发布更多具体的聚合和事件示例,将会有所帮助。通常,如果在您的情况下有意义,您可以创建更精细的事件。然后,不是在命令和事件之间建立1-1关系,你将拥有1-N关系,这与CQRS理论完全一致。
那么举个例子:
CreateInvoice : Command
- InvoiceId
- Customer (10 fields)
- Address (5 more fields)
- InvoceLine[] (where each InvoiceLine also have 10 fields or so)
- Rest of 100 or so fields
InvoiceCreated : Event
- InvoiceId
- Customer (10 fields)
- Address (5 more fields)
- InvoceLine[] (where each InvoiceLine also have 10 fields or so)
- Total
- Rest of 100 or so fields
在Command Handler中:
void Handle(CreateInvoce cmd)
{
var invoice = new Invoice(cmd.InvoiceId, cmd.Customer, cmd.Address, cmd.Lines ....)
uow.Register(invoice);
}
只会引发一个InvoceCreated事件。
相反,您可以拥有更精细的事件:
InvoiceCreated : Event
- InvoiceId
- Customer
- Address
InvoiceLineAdded
- InvoiceId
- Item
- Vat
- Subtotal
- Etc
然后在Command Handler中:
void Handle(CreateInvoce cmd)
{
var invoice = new Invoice(cmd.InvoiceId, cmd.Customer, cmd.Address);
foreach (var line in cmd.Lines)
{
invoice.AddLine(line.Item, line.Quantity, line.Price, ...);
}
uow.Register(invoice);
}
这里ctor将引发InvoiceCreated事件,AddLine方法将引发InvoiceLineAdded事件。 然后,您可以使用InvoiceLineChanged / InvoiceLineRemoved等事件,您可以将其与更新一起使用。
这将允许您拥有更多粒度事件,同时仍允许发出更粗粒度的命令。
当命令表示来自用户/系统PoV的原子动作时,大命令是可以的。
P.S。关于使用聚合作为模板,我不打扰它,而是创建数据结构作为累加器来收集中间状态。然后可以简单地序列化/反序列化。如果填写模板后没有任何行为 - 那么您根本不需要聚合。它只是一小段数据,稍后将用于创建聚合和运行业务规则。您可能正在使用此“模板”对象在多个请求之间存储用户输入的状态,如会话状态,对吧;)?
希望有所帮助。
答案 1 :(得分:0)
我在这里有一个类似的问题:How granular should a domain event be?
通常一个庞大的类表明你必须检查它的职责并根据SRP将它分成更小的类。通过聚合,域事件等...您可以遵循相同的方法。通过太多的课程,您可以考虑多个有界的上下文,依此类推......
问题的另一部分,即命令和事件粒度是业务决策。如果您的域名模型对您的大事件有意义,那么请使用它。否则你(或域专家)应该问自己,用户意图是什么,当前事件是否表达了它?只是一个非常简单的示例,您甚至可以拥有非常小的域事件:PhoneNumberChanged
甚至更小:PhoneNumberCorrected
,PhoneNumberMigrated
(所有这些事件只包含一个属性,新电话号码) 。据此,你也可以举办大型活动。 OFC。保持这些更难,一段时间后你可能开始对它们进行版本化,或者将整个事件存储迁移到另一个域结构下。
如果不了解问题,很难提供更多帮助。您可能应该告诉我们一些关于过去几年中您的巨大域名事件发生的事情,您之后是否遇到过问题?