为什么CQRS命令处理程序排除保存UnitOfWork



我一直在查看不同的CQRS samples,它们中的大多数都使用不保存UnitOfWork的命令处理程序(即Entity Framework中的DataContext)。类似这样的东西:

public void Handle(Command message)
{
var course = Mapper.Map<Command, Course>(message);
_db.Courses.Add(course);
}

保存(和事务提交)通常在处理请求时在后台进行。

我从许多CQRS的领导那里看到过这种方法,但我从未听说过它的理由

这种方法最大的问题是在返回处理程序调用后需要立即获取实体Id的情况(这种情况经常发生)。很明显,有一些方法可以解决它(例如,使用Guid,从数据库中预先请求唯一Id等),但看起来很笨拙。

但是这种方法的优点是什么?从理论上讲,如果每个请求有几个处理程序,那么不进行多次数据库往返可能会有所帮助。但这种情况并不经常发生。我想到的另一个优点是,我们不必键入例行的Save调用,然后让它自动发生。这有点不错,但它是否加重了身份证生成问题?

为什么CQRS命令处理程序排除保存UnitOfWork?

我认为它始于Evans,领域驱动设计,第6章领域对象的生命周期

有很多技术可以应对数据库访问的技术挑战。。。。

但即便如此,也要注意已经失去的东西。我们不再考虑领域模型中的概念。我们的代码将不再与业务通信,而是操纵数据检索技术。

在代码的这一点上,我们使用的是域模型,而不是数据模型

一个REPOSTORY减轻了客户端的巨大负担,客户端现在可以与一个简单的、意图揭示的界面对话,并询问它在模型方面需要什么

Evans在交易的背景下非常强调这一点

将事务控制权留给客户端。尽管REPOSTORY将插入数据库和从数据库中删除,但它通常不会提交任何内容。例如,在保存后提交是很诱人的,但客户端可能具有正确启动和提交工作单元的上下文。如果仓库不动手,交易管理会更简单。

一些附加说明:

这种方法最大的问题是在返回处理程序调用后需要立即获取实体Id的情况(这种情况经常发生)。

这通常表明你在解决错误的问题。参见Marc de Graauw,《没有人需要可靠的消息传递》。

命令处理程序的一个重要特性是它不返回任何内容(异常除外)。这是一个微妙的点,但如果你知道所有的命令处理程序都不会返回任何东西,那么你就可以节省大量的代码,简化或制作一个更健壮的代码库。

但是,如果您没有返回任何内容,那么在流程开始时就需要id。我认为使用GUID是一个很好的解决方案。

从数据库中预先获取唯一id充满了问题,如果可能的话,我会避免这种方法。

以下是一些优点:

  1. 所有命令处理程序都将有一个通用接口
  2. 因此,您可以编写支持所有命令处理程序的帮助程序代码。例如命令路由器、安全和权限检查、日志记录、性能监控、消息队列
  3. 它可以显著减少您需要编写的代码量
  4. 它可以显著降低您需要编写的代码的复杂性
  5. 测试更加简单和稳健

这些只是我脑海中的一小部分。

为什么不包括一个工作单元?

你可以是一个简短的答案。但我喜欢把坚持的责任从处理者身上转移出去。处理程序可能会调用它,但实际的持久性是在其他地方完成的(在大多数情况下,我更喜欢在事件存储中)。

最新更新