当我创建一个映射到具有标识主键的表的实体时,我不知道该ID,直到实体被持久化.实体是持久性无知的,所以当从实体中发布事件时,Id只是不知道 – 在调用context.SaveChanges()之后,它是神奇地设置的.那么如何/在哪里/什么时候可以把Id放在事件数据中?
我在想:
>在事件中包括对实体的引用.这将在域内工作,但在分布式环境中不必要地具有多个自主系统通过事件/消息进行通信.
>覆盖SaveChanges()以某种方式更新排入队列以便发布的事件.但事件是不可变的,所以这看起来很脏.
>删除身份字段并使用实体构造函数中生成的GUID.这可能是最简单的,但可能会影响性能,并使其他事情变得更加困难,如调试或查询(其中id =’B85E62C3-DC56-40C0-852A-49F759AC68FB’,无MIN,MAX等).这就是我在许多示例应用程序中看到的.
>混合方法 – 单独使用身份,主要用于外键和快速连接,但使用GUID作为唯一标识符,通过该唯一标识符,我将实体从应用程序中的存储库中拉出.
解决方法
简而言之,由于您遵循CQRS,您无疑有一个CreateSomethingCommand和相应的CreateSomethingCommandHandler实际执行创建新实例所需的步骤,并使用存储库(通过context.SaveChanges)持久化新对象.我将在这里提出SomethingCreated事件,而不是在域对象本身中.
一个解决您的问题,因为命令处理程序可以等待数据库操作完成,拉出标识值,更新对象,然后在事件中传递身份.但更重要的是,它还解决了“创建”对象的何时是棘手的问题?
在构造函数中提升域事件是不好的做法,因为构造函数应该是精简的,只需执行初始化.另外,在您的模型中,对象在分配了ID之前并不会真正创建.这意味着在构造函数执行后还需要额外的初始化步骤.如果你有多个步骤,你是否执行执行顺序(另一个反模式),或者在每个步骤中进行检查,以确认何时完成(ooh,smelly)?希望你能看到如何快速地螺旋出来.
所以,我的建议是从命令处理程序引发事件. (注意:即使您切换到GUID标识符,我将遵循此方法,因为您不应该从构造函数引发事件.)