spring-projects / spring-data-commons

Spring Data Commons. Interfaces and code shared between the various datastore specific implementations.
https://spring.io/projects/spring-data
Apache License 2.0
756 stars 662 forks source link

Support for exposing domain events from aggregate roots as Spring application events [DATACMNS-928] #1381

Closed spring-projects-issues closed 7 years ago

spring-projects-issues commented 7 years ago

Oliver Drotbohm opened DATACMNS-928 and commented

Context

One important element of Domain-Driven Design is exposing events important to the domain to other components. These events are produced by aggregates and usually are tied to state transitions of the aggregate. Right now, a typical pattern to produce those events is this:

@RequiredArgsConstructor
class MyComponent {

  private final @NonNull MyRepository repository;
  private final @NonNull ApplicationEventPublisher publisher;

  public void doSomething(MyAggregateRoot entity) {

    MyDomainEvent event = entity.someBusinessFunctionality();
    publisher.publishEvent(event);
    repository.save(entity);
  }
}

Problem

As you can see, the client needs to use ApplicationEventPublisher explicitly. If we provided a way for the aggregate root to expose the events to be published, the repository could take over the burden to publish the events.

Proposed solution

First of all we need means to let the aggregate root expose the events to be published. This could be an annotation based approach like this:

class MyAggregateRoot {

  @PublishedDomainEvents
  Collection<Object> domainEvents() {
    …
  }
}

The annotation name to be open for discussion, of course. We could support to return a Collection of events or a single one. The same could be achieved with an interface, of course.

The repository infrastructure could then inspect the aggregate root type for a method declaration with said annotation (or a type check for the interface pendant) and add an interceptor to the repository proxy that holds a reference to the ApplicationEventPublisher, grabs the events to publish from the aggregate and publishes them before forwarding the call


Referenced from: commits https://github.com/spring-projects/spring-data-commons/commit/a02174fd7bfce12c5883ed47ddd6cd9df40df35c, https://github.com/spring-projects/spring-data-commons/commit/0fc9770462a61060d170436d6e87e021cc096c21, https://github.com/spring-projects/spring-data-commons/commit/9ada55b050607e6000905ea9093e3026cacfacfe, https://github.com/spring-projects/spring-data-commons/commit/2c0941b41d25ac80dbeb4fd27ba7d72507738099, https://github.com/spring-projects/spring-data-commons/commit/cb6d2f7cc310b337b402219f93c64cce04cf0d1f, https://github.com/spring-projects/spring-data-commons/commit/c66cbb985f890626b48820d11b9277658c65b6cc, https://github.com/spring-projects/spring-data-commons/commit/bdf4738716b975c4f32c01591a5ccea4a7b40e01, https://github.com/spring-projects/spring-data-commons/commit/6927c29119c534dad8a0a331ad3f7387faf8a6df, https://github.com/spring-projects/spring-data-commons/commit/db17a28238041375582aa91d3687d85130cab80f, https://github.com/spring-projects/spring-data-commons/commit/afe95b48d2ebcd05a61cad9daaf273232b8c3c89, https://github.com/spring-projects/spring-data-commons/commit/806269858956d95ed9408d6de93f3632b30b7bae, https://github.com/spring-projects/spring-data-commons/commit/f6db714206a78b2321dcf1905108aac7ee1a853f, https://github.com/spring-projects/spring-data-commons/commit/50ea2ca6eac6c0d1a4e673742b20142f71cf0d6b, https://github.com/spring-projects/spring-data-commons/commit/c6f9c404722cd9889450a824788fe5dc9bfceade, https://github.com/spring-projects/spring-data-commons/commit/c52c45741a988dde87e5dadc6ba1fd4cde7f9ae4, https://github.com/spring-projects/spring-data-commons/commit/802eb7deef7f963b3168382f24ed02700c302565, https://github.com/spring-projects/spring-data-commons/commit/4177fb4569b3865398c90c64b16c1ed6281e7b94

Backported to: 1.13 RC1 (Ingalls)

2 votes, 5 watchers

spring-projects-issues commented 7 years ago

John Blum commented

I quite like this Ollie; I think this is a nice/clean design and approach