简体   繁体   English

如何使用 spring 正确发布 DDD 域事件?

[英]How to properly publish DDD domain events with spring?

I am trying to implement domain driven design in my project.我正在尝试在我的项目中实现领域驱动设计。 Here is my base Aggregate class:这是我的基本Aggregate class:

public abstract class UUIDAggregate {
    private final DomainEventPublisher domainEventPublisher;

    protected void publish(DomainEvent domainEvent) {
        domainEventPublisher.publish(domainEvent);
    }
}

Let's say we have UserAccount aggregate:假设我们有UserAccount聚合:

public class UserAccount extends UUIDAggregate {
    private String email;
    private String username;
    private String password;
    private String firstName;
    private String lastName;

    public void update() {
        publish(new DomainEventImpl());
    }
}

Here is my DomainEventPublisher :这是我的DomainEventPublisher

public interface DomainEventPublisher {
   void publish(DomainEvent event);
}

Here is DomainEventPublisherImpl :这是DomainEventPublisherImpl

@Component
public class DomainEventPublisherImpl implements DomainEventPublisher{
    @Autowired
    private ApplicationEventPublisher publisher;

    public void publish(DomainEvent event){
        publisher.publishEvent(event);
    }
}

Now, this seems like a good idea, the domain is separated from implementation but this does not work.现在,这似乎是个好主意,域与实现分开,但这不起作用。 DomainEventPublisher cannot be Autowired because UUIDAggregate is not a @Component or @Bean . DomainEventPublisher不能自动@Component ,因为UUIDAggregate不是Autowired@Bean One solution would be to create DomainService and publish event there but that seems like leaking of domain to domain service and if I go that way, I am going to anemic model.一种解决方案是创建DomainService并在那里发布事件,但这似乎是将域泄漏到域服务,如果我 go 那样,我会贫血 model。 Also what I can do is to pass DomainEventPublisher as a parameter to every aggregate but that also does not seems like a good idea.另外,我可以做的是将DomainEventPublisher作为参数传递给每个聚合,但这似乎也不是一个好主意。

One idea would be to have a factory for domain objects:一个想法是为域对象建立一个工厂:

@Component
class UserAccountFactoryImpl implements UserAccountFactory {
    @Autowired
    private DomainEventPublisher publisher;

    @Override
    public UserAccount newUserAccount(String email, String username, ...) {
        return new UserAccount(email, username, ..., publisher);
    }
}

Then your code creating a domain object is "publisher-free":那么您创建域 object 的代码是“无发布者”:

UserAccount userAccount = factory.newUserAccount("john@example.com", ...);

Or you might slightly change the design of the event-publishing:或者您可能会稍微更改事件发布的设计:

public abstract class UUIDAggregate {
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    protected void publish(DomainEvent domainEvent) {
        domainEvents.add(domainEvent);
    }
    public List<DomainEvent> domainEvents() {
        return Collections.unmodifiableList(domainEvents);
    }
}

@Component
class UserAccountServiceImpl implements UserAccountService {
    @Autowired
    private DomainEventPublisher publisher;

    @Override
    public void updateUserAccount(UserAccount userAccount) {
        userAccount.update();

        userAccount.domainEvents().forEach(publisher::publishEvent);
    }
}

This is different from your proposal: the service publishes the events, but doesn't create then - the logic stays in the domain object.这与您的建议不同:服务发布事件,但不创建事件 - 逻辑保留在域 object 中。

Further, you can change your publisher to minimize the boiler-plate code:此外,您可以更改发布者以最小化样板代码:

public interface DomainEventPublisher {
   void publish(UUIDAggregate aggregate);
}

Vaughn Vernon in his book IDDD just uses singleton like this: Vaughn Vernon 在他的《IDDD》一书中只使用了 singleton,如下所示:

DomainEventPublisher.instance().register(...);

DomainEventPublisher.instance().publish(...);

I know this approach doesn't use spring injection but it's much simplier than passing publisher to every aggregate and not that hard to test.我知道这种方法不使用 spring 注入,但它比将发布者传递给每个聚合要简单得多,而且测试起来也不难。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM