kir*_*rpt 13 domain-driven-design spring-boot mapstruct
我努力寻找并实施以下问题的最佳实践:引发创建域事件(通知创建聚合的事件)的最佳位置在哪里。例如,如果我们在有界上下文中有订单聚合,我们希望在创建订单时通知所有感兴趣的各方。该事件可以是OrderCreatedEvent。
我首先尝试的是在构造函数中引发此事件(我在每个聚合中都有一个域事件集合)。这样只有我们创建订单的时候才可以。因为当我们将来想对这个聚合做任何事情时,我们将通过构造函数创建它的新实例。然后OrderCreatedEvent将再次引发,但事实并非如此。
但是,我认为在应用程序层中引发事件是可以的,但这是一种反模式(域事件应该只存在于域中)。也许可以选择使用一个Create方法,将 OrderCreatedEvent 添加到其域事件列表中,并在创建订单时在应用程序层中调用它。
我在互联网上发现的有趣事实是,在构造函数中引发域事件是一种反模式,这意味着最后描述的选项(具有Create 方法)将是最好的方法。
我使用 Spring Boot 作为应用程序,使用 MapStruct 作为将数据库/存储库实体映射到域模型聚合的映射器。此外,尝试找到一种方法来创建一个映射器,该映射器将跳过目标类的构造函数,但由于 Order 聚合的所有属性都是私有的,这似乎是不可能的。
通常构造函数仅用于对对象的字段进行赋值。这不是触发行为的正确位置,特别是当它们抛出异常或产生副作用时
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());
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,调用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);
});
}
}
Run Code Online (Sandbox Code Playgroud)
应用程序层负责决定如何完成此工作流程(创建聚合、持久化并发送一些事件),但所有步骤都是透明发生的。我可以向聚合根添加新属性、更改 DBMS 或更改事件契约,这不会更改这些代码行。应用层决定做什么,领域层决定如何做。工厂是领域层的一部分,事件是聚合状态的瞬态部分,从领域的角度来看,发布部分是透明的
看看这个问题! 在持久化聚合之前发布域事件是否安全?。但是,我认为在应用程序层中引发事件是可以的,但这是一种反模式(域事件应该只存在于域中)。- 领域事件存在于领域层,但应用程序层引用领域层并且可以轻松发出领域事件。
归档时间: |
|
查看次数: |
5477 次 |
最近记录: |