如何处理DDD + CQRS +事件采购方法中的业务规则?

zug*_*ugo 2 architecture domain-driven-design business-rules cqrs event-sourcing

我试图找出如何使用CQRS/ES方法处理复杂域模型.让我们假设我们有例如Order域实体,它处理订单的状态和行为.它有一个Status属性,带有用于在状态之间切换的转换规则(实现状态模式或任何其他类型的状态机).据DDD原理,该逻辑应在Order类(表示订单模型)来实现本身,具有类似的方法approve(),cancel(),ship()等.

看看这种体系结构的不同 公共 示例,结果表明域实体和聚合根是相同的,它处理状态和行为,甚至是自己对事件的预测.这不违反SRP吗?

但我的问题更具体:如果我想处理新命令(并应用新事件),我应该从事件流(即从写模型和写入数据库)重建实体并调用其行为方法(将事件应用于状态)处理业务规则?或者只是处理命令和事件本身,而没有任何写模型实体?

伪代码说明:

class ApproveOrderHandler
{
    private EventStore eventStore

    // ...

    public void handle(ApproveOrder event)
    {
        Order order = this.eventStore.findById(event.getOrderId()); // getting order projection from event store
        order.approve(); // handling business logic
        this.eventStore.save(order.releaseEvents()); // save new events (OrderApproved)
    }
}

class Order extends AbstractAggregate
{
    private Uuid id;

    private DateTime takenAt;

    private OrderStatus status;

    // ...

    public void approve()
    {
        this.status.approve(); // business rules blah blah
        this.Apply(new OrderApproved(this.id)); // applying event
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

是不是太过分了?

我应该如何处理事件采购中的实体之间的关系?如果它们仅存在于"读取模型"中,则域实体类中没有任何意义.

编辑:或者我应该将状态快照存储在"读取数据库"中并从中恢复实体以进行操作?但它打破了"不同的读写模式"的想法......

EDIT2:修复了读/写模型错误

Voi*_*son 6

TL; DR

但我的问题更具体:如果我想处理新命令(并应用新事件),我应该从事件流(即从写模型和写入数据库)重建实体并调用其行为方法(将事件应用于状态)处理业务规则?

是.

或者只是处理命令和事件本身,而没有任何写模型实体?

没有.

再一次,有感觉

命令处理程序位于应用程序组件中; 业务模型位于域组件中.

保持这些组件分离的动机:使模型替换具有成本效益.领域专家关心的是业务获胜的领域是领域模型.我们不期望一次编写业务模型并始终保持正确 - 更有可能我们将更多地了解我们希望模型如何工作,因此定期对模型进行改进.因此,重要的是没有太大的阻力来替换另一个版本的模型 - 我们希望替换变得容易; 我们希望将更改所需的工作量反映在我们获得的业务价值中.

因此,我们希望将好东西与"管道"分开.

保持域组件中的所有业务逻辑可以让您轻松获胜; 首先,您不必猜测业务逻辑所处的位置 - 用例的具体细节是否容易或困难,业务逻辑将在订单中,而不是其他任何地方.其次,因为业务逻辑不在命令处理程序中,所以您不必担心创建一堆测试双精度来满足这些依赖性要求 - 您可以直接测试域模型.

那么,我们使用处理程序来重构实体并调用它们的业务逻辑方法,而不是处理业务逻辑本身?

几乎 - 我们使用存储库来重构实体和聚合来处理业务逻辑.编排器的作用是编排 ; 它是数据模型和模型之间的粘合剂.