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

Spa*_*vić 5 java spring domain-driven-design

我正在尝试在我的项目中实现域驱动设计。这是我的基Aggregate类:

public abstract class UUIDAggregate {
    private final DomainEventPublisher domainEventPublisher;

    protected void publish(DomainEvent domainEvent) {
        domainEventPublisher.publish(domainEvent);
    }
}
Run Code Online (Sandbox Code Playgroud)

假设我们有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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的DomainEventPublisher

public interface DomainEventPublisher {
   void publish(DomainEvent event);
}
Run Code Online (Sandbox Code Playgroud)

这是DomainEventPublisherImpl

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

    public void publish(DomainEvent event){
        publisher.publishEvent(event);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,这似乎是一个好主意,域与实现分离,但这不起作用。DomainEventPublisher不可能是Autowired因为UUIDAggregate不是@Component@Bean。一种解决方案是在那里创建DomainService和发布事件,但这似乎将域泄漏到域服务,如果我这样做,我将进入贫血模型。此外,我可以做的是将DomainEventPublisher参数作为参数传递给每个聚合,但这似乎也不是一个好主意。

ttu*_*lka 3

一个想法是为域对象建立一个工厂:

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

    @Override
    public UserAccount newUserAccount(String email, String username, ...) {
        return new UserAccount(email, username, ..., publisher);
    }
}
Run Code Online (Sandbox Code Playgroud)

那么创建域对象的代码是“无发布者的”:

UserAccount userAccount = factory.newUserAccount("john@example.com", ...);
Run Code Online (Sandbox Code Playgroud)

或者您可以稍微更改事件发布的设计:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这与您的建议不同:服务发布事件,但不创建事件 - 逻辑保留在域对象中。

此外,您可以更改发布者以最小化样板代码:

public interface DomainEventPublisher {
   void publish(UUIDAggregate aggregate);
}
Run Code Online (Sandbox Code Playgroud)