【发布时间】:2014-05-25 10:22:48
【问题描述】:
我们正在使用事件溯源并从事件流中构建聚合。 我有 2 个聚合 - A1 和 A2。 A1 用作模板以创建 A2。 A1 的尺寸可能相当大。 事件溯源的基本思想是确保对应用程序状态的每次更改都被捕获到事件对象中。 所以要保存 A2,我们必须在第一个事件中存储大量信息。
这种情况是常见的还是从模板创建不是个好主意? 有没有更好的办法解决?
【问题讨论】:
标签: cqrs aggregateroot event-sourcing
我们正在使用事件溯源并从事件流中构建聚合。 我有 2 个聚合 - A1 和 A2。 A1 用作模板以创建 A2。 A1 的尺寸可能相当大。 事件溯源的基本思想是确保对应用程序状态的每次更改都被捕获到事件对象中。 所以要保存 A2,我们必须在第一个事件中存储大量信息。
这种情况是常见的还是从模板创建不是个好主意? 有没有更好的办法解决?
【问题讨论】:
标签: cqrs aggregateroot event-sourcing
如果您发布更多关于聚合和事件的具体示例,将会更有帮助。一般来说,如果在您的情况下有意义,您可以创建更精细的事件。那么 Command 和 Event 之间不是 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
在命令处理程序中:
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
然后在命令处理程序中:
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 的原子操作时。
附:关于使用聚合作为模板,我不会为此烦恼,而是创建数据结构作为累加器来收集中间状态。然后可以简单地对其进行序列化/反序列化。如果填充模板后没有任何行为 - 那么您根本不需要聚合。它只是一个数据块,稍后将用于创建聚合和运行业务规则。您可能正在使用这个“模板”对象来存储用户在多个请求之间输入的状态,例如会话状态,对吧;)?
希望对您有所帮助。
【讨论】:
我在这里也有类似的问题:How granular should a domain event be?
通常一个巨大的类表明你必须检查它的职责并根据 SRP 将它分成更小的类。通过聚合、领域事件等......您可以采用相同的方法。通过太多的类,你可以考虑多个有界上下文,等等......
问题的另一部分,即命令和事件粒度是业务决策。如果您的大型活动对您的领域模型有意义,那么请使用它。否则,您(或领域专家)应该问自己,用户意图是什么,当前事件是否表达了它?只是一个非常简单的示例,您甚至可以拥有非常小的域事件:PhoneNumberChanged 或更小:PhoneNumberCorrected、PhoneNumberMigrated(它们都只包含一个属性,即新电话号码)。据此,您也可以举办大型活动。办公室。维护它们要困难得多,一段时间后您可能会开始对它们进行版本控制,或者将整个事件存储迁移到另一个域结构下。
在不了解问题的情况下很难提供更多帮助。您可能应该告诉我们一些关于您在过去几年中发生的大型域事件的情况,您后来有什么问题吗?
【讨论】: