Occurrent 0.20.0 is released with two major themes:
- Spring Boot 4.0.4 and Jackson 3 as the new main path for Spring-based Occurrent applications
StreamReadFilter,ExecuteOptions, and related ApplicationService/Kotlin improvements for more explicit and efficient command handling
These changes work well together: Boot 4 + Jackson 3 is now the preferred stack for new applications, while StreamReadFilter and ExecuteOptions make it easier to express filtered reads and synchronous side effects explicitly at the ApplicationService boundary.
Spring Boot 4 and Jackson 3
Occurrent 0.20.0 upgrades the Spring Boot line to 4.0.4 and introduces a new Jackson 3-native converter lane.
Highlights:
- Added
org.occurrent:cloudevent-converter-jackson3as the new Jackson 3-native CloudEvent converter artifact. - The existing
org.occurrent:cloudevent-converter-jacksonartifact remains available as a compatibility lane for incremental migration. - The Spring Boot starter now supports both lanes while keeping the same Spring-facing API, including
@EnableOccurrentand theoccurrent.*property namespace. - Examples and starter-oriented setup are now updated to the Spring Boot 4 / Jackson 3 path.
- Remaining example usage of Jackson default typing has been replaced with explicit CloudEvent converter configuration.
Upgrade guidance:
- New applications should use Spring Boot 4 together with Jackson 3.
- Existing applications that already depend on the Jackson 2 converter API can continue to use that lane while migrating gradually.
StreamReadFilter and ExecuteOptions
Occurrent 0.20.0 also introduces a set of related API improvements around filtered stream reads and clearer ApplicationService execution options.
Why this matters:
StreamReadFilterlets supported event stores read only the subset of events that a command or use case actually depends on. This can reduce unnecessary IO and deserialization work when a stream contains many event types but a particular operation only needs a few of them.ExecuteOptionsmakes it possible to combine filtered reads and synchronous side effects in a single, explicitApplicationServiceAPI instead of relying on many overloaded methods.- Kotlin users now also get clearer collection-oriented names such as
executeSequence(...)andexecuteList(...), plus direct-import helpers likesideEffect(...),filter(...), and namespaced typed filters underExecuteFilters.
Highlights:
- Added
org.occurrent.eventstore.api.StreamReadFilter. - Added optional capability interface
org.occurrent.eventstore.api.blocking.ReadEventStreamWithFilter. - Added optional capability interface
org.occurrent.eventstore.api.reactor.ReadEventStreamWithFilter. - Added filtered stream-read support in:
InMemoryEventStoreMongoEventStoreSpringMongoEventStoreReactorMongoEventStore
- Added
org.occurrent.application.service.blocking.ExecuteOptions. - Added
ApplicationService.execute(streamId, executeOptions, domainFunction)for blocking application services. - Added Kotlin helpers:
executeSequence(...)executeList(...)options()filter(...)sideEffect(...)sideEffectOnSequence(...)sideEffectOnList(...)
- Kotlin collection-oriented application-service usage now centers on
executeSequence(...)andexecuteList(...). - Added namespaced Kotlin typed filter helpers under
ExecuteFilters, for exampleExecuteFilters.type<NameDefined>()andExecuteFilters.excludeTypes(NameDefined::class, NameWasChanged::class).
For synchronous side effects, the preferred API is now ExecuteOptions.sideEffect(...). The older executePolicy(...) and executePolicies(...) helpers still exist, but they are no longer the primary recommended approach for new code.
A simple Java example:
WriteResult result = applicationService.execute(
streamId,
ExecuteOptions.<DomainEvent>options()
.filter(StreamReadFilter.type("com.acme.NameDefined"))
.sideEffect(newEvents -> newEvents.forEach(this::publish)),
domainFn
);
A simple Kotlin example:
applicationService.executeSequence(
streamId,
options().filter(ExecuteFilters.type<NameDefined>()).sideEffect(
{ event: NameDefined -> publish(event) }
)
) { events ->
decide(events)
}
Read the updated documentation for details, examples, and guidance on when filtered reads are safe to use.