Skip to content

实现命令

不用 CQRS 是不是就不需要领域命令了?

只要你建模出了领域命令,那就应该对应的实现领域命令,这是 DDD 的核心方法论,和是否使用 CQRS 并没有必然关系。如果你采用了事件风暴,那么一般也是要采用 CQRS 的,因为事件风暴在建模的时候就已经把领域模型和读模型区分开了,为使用 CQRS 实现系统做了准备了。

不管你用什么建模方法,最终都需要说明一件事情,就是领域模块之外的代码该如何让领域模块做什么。领域命令正好就是表达这个事情的对象。当然,你也可以给它起名叫“领域模块动作”“业务动作”“业务期望”等等,但是本质上都是一个东西。

所有领域命令都必须被 CommandHandler 领域服务处理吗?

当领域模块外向领域模块发送一个命令时,我们需要领域模块内有一个对象负责接收这个命令,并决定该执行哪些业务功能。即使这个命令只需被一个聚合执行,也需要这个对象负责来载入这个聚合来执行命令。从这个角度说,领域模块内始终有一个对象扮演了 CommandHandler 的角色。

当你采用某些 DDD 框架的时候,有可能框架里的对象帮你完成了 CommandHandler 的职责,因此你可能不需要手动去实现这样的对象。

执行命令的 CommandHandler 不是应用服务吗?

不是应用服务。在某些人的代码实现中,聚合的载入、存储,聚合间的协作等,被当成非领域逻辑,放到了所谓应用服务中。这种做法是错误的。领域对象间的协作逻辑,也是领域逻辑,不应该被泄露到领域模块之外。领域模块外,应该只要关心如何构建一个命令,如何将它发送到领域模块,并不需要关心领域模块内部是何时载入了哪些聚合执行了哪些业务动作,即应该对外部封装这些协作逻辑。

命令执行过程中可以调用另外一个命令吗?

可以。有的时候,一个命令的功能,可以分解为多个子功能,其中一些功能可以通过重用已有的命令实现,这当然是可以重用的。 类似下面的代码:

kotlin
class Sub1Cmd
class Sub1CmdHandler

class SubCmd2
class Sub2CmdHandler

class MyCmd
class MyCmdHandler(
  private val sub1CmdHandler: Sub1CmdHandler,
  private val sub2CmdHandler: Sub2CmdHandler
) {
  fun handle(cmd: MyCmd) {
    sum1CmdHandler.handle(makeSub1CmdFromMyCmd(cmd))
    sum2CmdHandler.handle(makeSub2CmdFromMyCmd(cmd))
  }
}

一个命令可以操作多个聚合吗?

可以。

一个命令执行过程中可以发出多个领域事件吗?

可以。

怎么实现批量处理?

首先,看如何给命令建模。如果批量处理里的多个处理之间是完全孤立的,批量处理和用户多次操作的效果在业务上完全一样,那么这个时候的“批量处理”,只是一种交互逻辑,并不是领域逻辑,没有必要为这种操作单独建立一个命令。反之,如果这些处理间存在业务约束,和用户一个一个操作是不同的,那么就应该单独建立一个命令来实现“批量处理”,因为不可能通过重用单个处理的命令来实现这个命令。

然后,根据模型来初步选择技术实现方案。如果每个命令只做单个处理,那么在领域模块外通过重复调用命令的方式来实现“批量”;否则,就在领域模块内实现。

最后,如果性能不够用,为了达到性能要求,需要做一些性能优化,比如提前预读取数据做缓存,异步处理以尽快给用户反馈进度,甚至是放弃使用领域模型,而是直接操作数据库。大部分情况下,即使是在外部循环的调用命令来实现批量处理,性能也是够用的。

领域命令可以有返回值吗?

领域命令只是一个值对象,表示了外部对领域模块的一个期望,并不存在返回值这种说法。执行领域模型确实可以有执行的结果,比如下单命令的执行结果,除了通过“已创建订单”这样的领域事件来体现,还可以通过返回一个新建的订单的 id 来体现。

我有一个查询功能,要使用领域对象的功能,那这个到底是命令还是查询?

这是一个查询,因为它确实没有引发领域对象的变更。不过在实现的时候,可以通过复用领域对象来实现这个查询。

领域命令该异步执行还是通过执行?

根据具体的技术实现,具体的场景来选择合适的实现方式。反正正确的实现功能就是对的了。

领域命令可以成功一半吗?

不可以。一个命令要么成功、要么没成功,不可能部分成功,而且命令的执行结果,应该是强一致的。因此,一般将一个命令放在一个数据库事务内执行。

可以在一个数据库事务内执行多个命令吗?

可以,这样单个命令依旧是原子的、强一致的。

在哪里划分事务边界?

在领域模块之外。