Occurrent 0.14.0 is now available. It’s features a new query DSL as well as a Spring Boot starter project to faster get started with Spring Boot. Changes are:
-
Non-backward compatible change: CloudEventConverter’s now has a third method that you must implement:
/** * Get the cloud event type from a Java class. * * @param type The java class that represents a specific domain event type * @return The cloud event type of the domain event (cannot be {@code null}) */ @NotNull String getCloudEventType(@NotNull Class<? extends T> type);
The reason for this is that several components, such as the subscription dsl, needs to get the cloud event type from the domain event class. And since this is highly related to “cloud event conversion”, this method has been added there to avoid complicating the API.
- Introduced the concept of CloudEventTypeMapper’s. A cloud event type mapper is component whose purpose it is to get the cloud event type from a domain event type and vice versa.
Cloud Event Type mappers are used by certain
CloudEventConverter
’s to define how they should derive the cloud event type from the domain event as well as a way to reconstruct the domain event type from the cloud event type. and the new domain queries DSL. You should use the same type mapper instance for all these components. To write a custom type mapper, depend on theorg.occurent:cloudevent-type-mapper-api
module and implement theorg.occurrent.application.converter.typemapper.CloudEventTypeMapper
(functional) interface. -
Introduced a blocking Query DSL. It’s a small wrapper around the EventStoreQueries API that lets you work with domain events instead of CloudEvents. Depend on the
org.occurrent:query-dsl-blocking
module and create an instance oforg.occurrent.dsl.query.blocking.DomainEventQueries
. For example:EventStoreQueries eventStoreQueries = .. CloudEventConverter<DomainEvent> cloudEventConverter = .. DomainEventQueries<DomainEvent> domainEventQueries = new DomainEventQueries<DomainEvent>(eventStoreQueries, cloudEventConverter); Stream<DomainEvent> events = domainQueries.query(Filter.subject("someSubject"));
There’s also support for skip, limits and sorting and convenience methods for querying for a single event:
Stream<DomainEvent> events = domainQueries.query(GameStarted.class, GameEnded.class); // Find only events of this type GameStarted event1 = domainQueries.queryOne(GameStarted.class); // Find the first event of this type GamePlayed event2 = domainQueries.queryOne(Filter.id("d7542cef-ac20-4e74-9128-fdec94540fda")); // Find event with this id
There are also some Kotlin extensions that you can use to query for a
Sequence
of events instead of aStream
:val events : Sequence<DomainEvent> = domainQueries.queryForSequence(GamePlayed::class, GameWon::class, skip = 2) // Find only events of this type and skip the first two events val event1 = domainQueries.queryOne<GameStarted>() // Find the first event of this type val event2 = domainQueries.queryOne<GamePlayed>(Filter.id("d7542cef-ac20-4e74-9128-fdec94540fda")) // Find event with this id
- Introducing spring boot starter project to easily bootstrap Occurrent if using Spring. Depend on
org.occurrent:spring-boot-starter-mongodb
and create a Spring Boot application annotated with@SpringBootApplication
as you would normally do. Occurrent will then configure the following components automatically:- Spring MongoDB Event Store instance (
EventStore
) - A Spring
SubscriptionPositionStorage
instance - A durable Spring MongoDB competing consumer subscription model (
SubscriptionModel
) - A Jackson-based
CloudEventConverter
- A
GenericApplication
instance (ApplicationService
) - A subscription dsl instance (
Subscriptions
) - A reflection based type mapper that uses the fully-qualified class name as cloud event type (you should absolutely override this bean for production use cases) (
CloudEventTypeMapper
) For example, by doing:@Bean public CloudEventTypeMapper<GameEvent> cloudEventTypeMapper() { return ReflectionCloudEventTypeMapper.simple(GameEvent.class); }
This will use the “simple name” (via reflection) of a domain event as the cloud event type. But since the package name is now lost, the
ReflectionCloudEventTypeMapper
will append the package name ofGameEvent
when converting back into a domain event. This only works if all your domain events are located in the exact same package asGameEvent
. If this is not the case you need to implement a more advancedCloudEventTypeMapper
such as:class CustomTypeMapper : CloudEventTypeMapper<GameEvent> { override fun getCloudEventType(type: Class<out GameEvent>): String = type.simpleName override fun <E : GameEvent> getDomainEventType(cloudEventType: String): Class<E> = when (cloudEventType) { GameStarted::class.simpleName -> GameStarted::class GamePlayed::class.simpleName -> GamePlayed::class // Add all other events here!! ... else -> throw IllegalStateException("Event type $cloudEventType is unknown") }.java as Class<E> }
See
org.occurrent.springboot.OccurrentMongoAutoConfiguration
if you want to know exactly what gets configured.
- Spring MongoDB Event Store instance (
- Upgraded spring-boot from 2.5.4 to 2.5.6.