域事件中的新实体ID

Pei*_*ein 12 domain-driven-design entity-framework cqrs domain-events

我正在使用CQRS和域事件概念构建具有域模型的应用程序(但没有事件源,只是普通的旧SQL).SomethingChanged类型的事件没有问题.然后我陷入了实施SomethingCreated事件的困境.

当我创建一个映射到具有身份主键的表的实体时,在实体被持久化之前我不知道Id.实体是持久性无知的,所以当从实体内部发布事件时,Id只是不知道 - 它只是在调用context.SaveChanges()之后神奇地设置.那么我如何/何时/何时将Id放入事件数据中?

我在考虑:

  • 包括事件中对实体的引用.这可以在域内工作,但不一定在具有通过事件/消息进行多个自治系统通信的分布式环境中.
  • 重写SaveChanges()以某种方式更新排队以进行发布的事件.但事件是不可改变的,所以这看起来很脏.
  • 删除标识字段并使用实体构造函数中生成的GUID.这可能是最简单的,但可能会影响性能并使其他事情变得更难,例如调试或查询(where id = 'B85E62C3-DC56-40C0-852A-49F759AC68FB',不MIN,MAX等等).这就是我在许多示例应用程序中看到的内容.
  • 混合方法 - 单独保留标识并将其主要用于外键和更快的连接,但使用GUID作为唯一标识符,通过该标识符从应用程序中的存储库中提取实体.

Son*_*ate 10

我个人喜欢GUID的唯一标识符,特别是在数字ID导致问题的多用户分布式环境中.因此,我从不使用数据库生成的标识列/属性,这个问题就消失了.

除此之外,由于您正在关注CQRS,因此您无疑拥有CreateSomethingCommand和相应的CreateSomethingCommandHandler,它们实际执行创建新实例所需的步骤并使用存储库(通过context.SaveChanges)保留新对象.我将在这里提出SomethingCreated事件,而不是在域对象本身.

首先,这解决了您的问题,因为命令处理程序可以等待数据库操作完成,提取标识值,更新对象然后在事件中传递标识.但是,更重要的是,它还解决了对象"创建"的确切时间的棘手问题?

在构造函数中引发域事件是不好的做法,因为构造函数应该是精简的并且只是执行初始化.另外,在您的模型中,在分配了ID之前,不会真正创建对象.这意味着在执行构造函数之后还需要执行其他初始化步骤.如果你有多个步骤,你是否强制执行执行顺序(另一个反模式)或者检查每个步骤以确认它们何时完成(哦,臭)?希望你能看到它如何迅速失控.

所以,我的建议是从命令处理程序中引发事件.(注意:即使你切换到GUID标识符,我也会遵循这种方法,因为你永远不应该从构造函数中引发事件.)

  • "所以,我的建议是从命令处理程序中提升事件"=>事件应该是域模型的一部分.而不是一个糟糕的构造函数,为了增加意义,我将创建一个静态工厂:`create`在成功的情况下提升事件. (5认同)