Skip to content

v1.3.0

Compare
Choose a tag to compare
@idugalic idugalic released this 17 Dec 23:21
· 2 commits to v1 since this release

Version 1.3.0 is a functional and async version of fmodel libraries optimized for Event sourcing and CQRS.
Please find the README/DOC here. It is maintained on the v1 branch.

Version 2.*.* is a reactive version (Flow included) of the libraries optimized for Event sourcing and CQRS. It is maintained on the main branch

What's Changed

  • Increasing modularity and pluggability of the Application module
    The application module has interfaces only.
    The extensions to the interfaces will be provided in separate modules:
    • application vanilla extension (kotlin),
    • application arrow extension (kotlin, arrow),
    • your custom module

Application

interface EventSourcingAggregate<C, S, E> : IDecider<C, S, E>, EventRepository<C, E> {

    fun Sequence<E>.computeNewEvents(command: C): Sequence<E> =
        decide(command, fold(initialState) { s, e -> evolve(s, e) })
}

Application Vanilla

suspend fun <C, S, E> EventSourcingAggregate<C, S, E>.handle(command: C): Sequence<E> =
    command
        .fetchEvents()
        .computeNewEvents(command)
        .save()

Application Arrow

suspend fun <C, S, E> EventSourcingAggregate<C, S, E>.handleEither(command: C): Either<Error, Sequence<E>> {

    suspend fun C.eitherFetchEventsOrFail(): Either<Error.FetchingEventsFailed, Sequence<E>> =
        Either.catch {
            fetchEvents()
        }.mapLeft { throwable -> Error.FetchingEventsFailed(throwable) }

    suspend fun E.eitherSaveOrFail(): Either<Error.StoringEventFailed<E>, E> =
        Either.catch {
            this.save()
        }.mapLeft { throwable -> Error.StoringEventFailed(this, throwable) }

    suspend fun Sequence<E>.eitherSaveOrFail(): Either<Error.StoringEventFailed<E>, Sequence<E>> =
        either<Error.StoringEventFailed<E>, List<E>> {
            this@eitherSaveOrFail.asIterable().map { it.eitherSaveOrFail().bind() }
        }.map { it.asSequence() }

    fun Sequence<E>.eitherComputeNewEventsOrFail(command: C): Either<Error, Sequence<E>> =
        Either.catch {
            computeNewEvents(command)
        }.mapLeft { throwable ->
            Error.CalculatingNewEventsFailed(this.toList(), throwable)
        }

    // Arrow provides a Monad instance for Either. Except for the types signatures, our program remains unchanged when we compute over Either. All values on the left side assume to be Right biased and, whenever a Left value is found, the computation short-circuits, producing a result that is compatible with the function type signature.
    return either {
        command
            .eitherFetchEventsOrFail().bind()
            .eitherComputeNewEventsOrFail(command).bind()
            .eitherSaveOrFail().bind()
    }
}