DDD:如何引发“创建”领域事件

13
我在以下问题中苦苦寻找和实施最佳实践:在哪里提出创建领域事件(通知聚合物创建的事件)最为合适。例如,如果我们在有界上下文中有“订单聚合”,我们希望在创建订单时通知所有感兴趣的方。事件可以是“OrderCreatedEvent”。
我首先尝试在构造函数中提出此事件(每个聚合都有一个领域事件集合)。这种方法只适用于创建订单时。因为当我们将来想要对此聚合进行任何操作时,我们将通过构造函数创建新实例。然后,“OrderCreatedEvent”将再次被提出,但这是不正确的。
但是,我认为在应用程序层中提高事件是一种反模式(领域事件应仅存在于域中)。也许拥有一个“Create”方法,只需将“OrderCreatedEvent”添加到其域事件列表中,并在创建订单时在应用程序层调用它是一种选择。
我在互联网上发现的有趣事实是,在构造函数中引发域事件是一种反模式,这意味着最后描述的选项(拥有“Create method”)将是最佳方法。
我正在使用Spring Boot应用程序和MapStruct映射器将数据库/存储库实体映射到域模型聚合。还试图找到一种方法创建一个映射器,可以跳过目标类的构造函数,但由于订单聚合的所有属性都是私有的,因此似乎是不可能的。

1
我遇到了同样的问题。你最终采用了什么解决方案? - kazbeel
2个回答

6

通常构造函数仅用于为对象的字段进行赋值。这不是触发行为的正确位置,特别是当它们抛出异常或具有副作用时。

DDD理论家(从Eric Evans开始)建议实现工厂来创建聚合。例如,工厂方法可以调用聚合构造函数(并将聚合与子域对象连接起来),还可以注册事件。

从应用程序层发布事件本身并不是反模式。应用程序服务可以依赖于领域事件发布者,重要的是不应该由应用程序层决定发送哪个事件。

总之,在像Java Spring Boot这样的技术栈中,并且支持领域事件,您的代码可能如下所示:

public class MyAggregate extends AbstractAggregateRoot {
    public static MyAggregate create() {
        MyAggregate created = new MyAggregate();
        created.registerEvent(new MyAggregateCreated());
        return created;
    }
}

public class MyApplicationService {
    @Autowired private MyAggregateRepository repository;

    public void createAnAggregate() {
        repository.save(MyAggregate.create());
    }
}

请注意,在调用repository.save()之后,事件发布会自动发生。这里的缺点是,当您使用数据库生成的标识符时,聚合id不会出现在事件负载中,因为它是在持久化聚合之后关联的。如果我将应用程序服务代码更改为以下内容:

public class MyApplicationService {
    @Autowired private MyAggregateRepository repository;
    @Autowired private ApplicationEventPublisher publisher;


    public void createAnAggregate() {
        repository.save(MyAggregate.create()).domainEvents().forEach(evt -> {
            publisher.publish(evt);
        });
    }
}

应用程序层负责决定如何完成此工作流程(创建聚合、持久化并发送某些事件),但所有步骤都是透明的。我可以向聚合根添加新属性,更改数据库管理系统或更改事件契约,这不会改变这些代码行。应用程序层决定要做什么,而领域层则决定如何做。 工厂是领域层的一部分,事件是聚合状态的瞬态部分,发布部分从领域角度来看是透明的。


1

看看这个问题!在持久化聚合之前发布域事件是否安全?。然而,我认为在应用层中触发事件是可以的,但这是一种反模式(域事件应该只存在于域中)。- 域事件存在于域层中,但应用层引用了域层并且可以轻松地发出域事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接