应该使用方法参数还是通过引用将命令信息传递给聚合?

Should command info be passed to aggregate using method parameters or by reference?

我正在努力实现一个基本的 CQRS+ES 应用程序。我看过很多例子,但我不明白命令处理程序和聚合之间的路由。

在一些例子中,工作是这样完成的:

XCommandHandler:

void Handle(XCommand command) {
   var aggregate = this.repository.Find<Aggregate>(command.aggId);

   aggregate.InvokeSomeBusinessLogic(command.property1, command.property2);
   this.repository.Save(aggregate);
}

但其他人以另一种方式做:

XCommandHandler:

void Handle(XCommand command) {
   var aggregate = this.repository.Find<Aggregate>(command.aggId);

   aggregate.InvokeSomeBusinessLogic(command);
   this.repository.Save(aggregate);
}

最好的方法是什么,尤其是当命令中有很多属性(15 个或更多)时?

这不是关于 CQRS+ES 的问题,而是关于 API 整体设计的问题。正如 Clean Code 所解释的,没有参数的方法优于带有一个参数的方法,后者又优于带有两个参数的方法,等等。

盲目遵守该规则应该总是使您 select 您的第二个选择:

aggregate.InvokeSomeBusinessLogic(command);

然而,盲目遵守任何规则很少是个好主意。在这种情况下,将 Interface Segregation Principle 视为反作用力是个好主意:

Clients should not be forced to depend on methods they do not use.

因为 getter 和 setter 也是方法,我认为这个原则也适用于命令对象。

因此,如果您有 15 个或更多属性,但所讨论的方法只需要其中两个,也许只传递这两个值会更好。

另一件事完全是,如果您有一个具有 15 个属性的命令对象,您可能需要重新考虑您的设计。 CQRS+ES 背后的基本假设之一是应用程序存在 task-based UIs,这确保每个命令(和相应的事件)都是 'small'.


正在用链接编辑 Mark 的评论,因为其中一个链接已损坏:

更新聚合信息不是基于任务的;这是CRUD。 Udi Dahan 的这两篇文章也许会对您有所帮助。

Queries, Patterns, and Search – food for thought

Tasks, Messages, & Transactions – the holy trinity

我原则上同意马克。然而,我要指出的是,CQRS 是从领域驱动设计领域发展而来的。他们强调语言的重要性 (ubiquitous language)。您可以在命令对象的实际名称中捕捉意图和意义。

在实际层面上,如果从多个地方调用,通过添加参数来重构方法会变得非常痛苦。这可以通过单个对象显着缓解。

我有几篇相关的文章可能对您有所帮助。 Aggregate Root – How to Build One for CQRS and Event Sourcing and 6 Code Smells with your CQRS Events – and How to Avoid Them。虽然最后一个是关于事件的,但许多原则都适用。

希望它们对您有所帮助。