All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonTest.com.fraktalio.fmodel.application.EventSourcedAggregateTest.kt Maven / Gradle / Ivy

Go to download

Fmodel provides just enough tactical Domain-Driven Design patterns, optimised for Event Sourcing and CQRS. The application library orchestrates the execution of the logic by loading state, executing domain components and storing new state. It is an Arrow (FP library for Kotlin) flavored implementation.

There is a newer version: 3.6.0
Show newest version
package com.fraktalio.fmodel.application

import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.Either.Right
import com.fraktalio.fmodel.application.examples.numbers.NumberRepository
import com.fraktalio.fmodel.application.examples.numbers.even.command.EvenNumberRepository
import com.fraktalio.fmodel.application.examples.numbers.even.command.evenNumberRepository
import com.fraktalio.fmodel.application.examples.numbers.numberRepository
import com.fraktalio.fmodel.domain.IDecider
import com.fraktalio.fmodel.domain.combine
import com.fraktalio.fmodel.domain.examples.numbers.api.Description
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberCommand.EvenNumberCommand.AddEvenNumber
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberEvent.EvenNumberEvent.EvenNumberAdded
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberValue
import com.fraktalio.fmodel.domain.examples.numbers.even.command.evenNumberDecider
import com.fraktalio.fmodel.domain.examples.numbers.odd.command.oddNumberDecider
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList

/**
 * DSL - Given
 */
@FlowPreview
private fun  IDecider.given(
    repository: EventRepository,
    command: () -> C
): Flow> =
    eventSourcingAggregate(
        decider = this,
        eventRepository = repository
    ).handleEither(command())

/**
 * DSL - When
 */
@Suppress("unused")
private fun  IDecider.whenCommand(command: C): C = command

/**
 * DSL - Then
 */
private suspend infix fun  Flow>.thenEvents(expected: Iterable>) =
    toList() shouldContainExactly (expected)

/**
 * Event sourced aggregate test
 */
@FlowPreview
class EventSourcedAggregateTest : FunSpec({
    val evenDecider = evenNumberDecider()
    val oddDecider = oddNumberDecider()
    val combinedDecider = evenDecider.combine(oddDecider)
    val evenNumberRepository = evenNumberRepository() as EvenNumberRepository
    val numberRepository = numberRepository() as NumberRepository

    test("Event-sourced aggregate - add even number") {
        with(evenDecider) {
            evenNumberRepository.deleteAll()

            given(evenNumberRepository) {
                whenCommand(AddEvenNumber(Description("2"), NumberValue(2)))
            } thenEvents listOf(Right(EvenNumberAdded(Description("2"), NumberValue(2))))
        }
    }

    test("Event-sourced aggregate - add even number - error (large number > 1000)") {
        with(evenDecider) {
            evenNumberRepository.deleteAll()
            val command = AddEvenNumber(Description("2000"), NumberValue(2000))

            given(evenNumberRepository) {
                whenCommand(command)
            } thenEvents listOf(Left(Error.CommandHandlingFailed(command)))
        }

    }

    test("Combined Event-sourced aggregate - add even number") {
        with(combinedDecider) {
            numberRepository.deleteAll()

            given(numberRepository) {
                whenCommand(AddEvenNumber(Description("2"), NumberValue(2)))
            } thenEvents listOf(Right(EvenNumberAdded(Description("2"), NumberValue(2))))
        }
    }


})




© 2015 - 2025 Weber Informatics LLC | Privacy Policy