Your own framework
The parts in layer 1 are small on purpose, so that you can combine them into a framework of your own, shaped after the way your business works. A framework like that decides things for your team once (how a command finds its stream, how a reaction turns an event into a new command, how a query reads events) and leaves only the domain code to write.
Here’s a small example. It sends each command to a pure domain function through the ApplicationService, and lets a reaction to an event send a new command through a subscription:
class BoundedContext<C : Any, E : Any>( private val applicationService: ApplicationService<E>, private val subscriptions: Subscriptions<E>, private val streamIdOf: (C) -> String, private val decide: (List<E>, C) -> List<E>,) { fun dispatch(command: C) { applicationService.execute(streamIdOf(command)) { history -> decide(history, command) } }
fun <E1 : E> react(reactionId: String, eventType: Class<E1>, reaction: (E1) -> C?) { subscriptions.subscribe(reactionId, eventType) { event -> reaction(event)?.let(::dispatch) } }}Your team then writes only the domain code:
val game = BoundedContext(applicationService, subscriptions, GameCommand::gameId, ::decide)game.react("start-next-round", RoundEnded::class.java) { event -> StartRound(event.gameId) }game.dispatch(StartGame(gameId))A real framework has more to decide, for example what happens when a reaction fails, which subscription model makes reactions survive a restart, and how commands are retried. Those are the choices that should follow your business, which is why Occurrent leaves them to you.
The occurrent-hederlig module (source) is an early experiment with this idea. It defines a module as a set of features, each with commands, subscriptions and queries, and runs it on the ApplicationService, Subscriptions and DomainEventQueries. It is unfinished and not ready for use.
