Skip to content

Commit

Permalink
MemoryView implementation -- Renaming and Arrow
Browse files Browse the repository at this point in the history
- TODO: missing arrow tests
- TODO: Actor?
  • Loading branch information
DomenicDev committed Dec 13, 2024
1 parent 4e49933 commit e9f081e
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 97 deletions.
4 changes: 4 additions & 0 deletions application-arrow/api/application-arrow.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
public final class com/fraktalio/fmodel/application/EphemeralViewArrowExtensionKt {
public static final fun handleWithEffect (Lcom/fraktalio/fmodel/application/ViewStateComputation;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/fraktalio/fmodel/application/EventSourcingAggregateArrowExtensionKt {
public static final fun handleOptimisticallyWithEffect (Lcom/fraktalio/fmodel/application/EventSourcingLockingAggregate;Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun handleOptimisticallyWithEffect (Lcom/fraktalio/fmodel/application/EventSourcingLockingAggregate;Ljava/lang/Object;Ljava/util/Map;)Lkotlinx/coroutines/flow/Flow;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.fraktalio.fmodel.application

import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.fold


suspend fun <S, E, Q, MV> MV.handleWithEffect(query: Q): Either<Error, S>
where MV : ViewStateComputation<S, E>, MV : EphemeralViewRepository<E, Q> {

fun Q.fetchEventsWithEffect(): Either<Error, Flow<E>> =
either {
catch({
fetchEvents()
}) {
raise(Error.FetchingEventsFailed(query, it))
}
}

suspend fun Flow<E>.computeStateWithEffect(): Either<Error, S> =
either {
catch({
fold(initialState) { s, e -> evolve(s, e) }
}) {
raise(Error.CalculatingNewViewStateFailed(this@computeStateWithEffect, it))
}
}

return either {
query.fetchEventsWithEffect().bind()
.computeStateWithEffect().bind()
}
}
8 changes: 4 additions & 4 deletions application-vanilla/api/application-vanilla.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
public final class com/fraktalio/fmodel/application/EphemeralViewExtensionKt {
public static final fun handle (Lcom/fraktalio/fmodel/application/ViewStateComputation;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/fraktalio/fmodel/application/EventSourcingAggregateActorExtensionKt {
public static final fun handleConcurrently (Lcom/fraktalio/fmodel/application/EventSourcingAggregate;Lkotlinx/coroutines/flow/Flow;IILkotlinx/coroutines/CoroutineStart;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow;
public static final fun handleConcurrently (Lcom/fraktalio/fmodel/application/EventSourcingAggregate;Lkotlinx/coroutines/flow/Flow;IILkotlinx/coroutines/CoroutineStart;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
Expand Down Expand Up @@ -106,10 +110,6 @@ public final class com/fraktalio/fmodel/application/MaterializedViewExtensionKt
public static final fun publishWithMetaDataTo (Lkotlinx/coroutines/flow/Flow;Lcom/fraktalio/fmodel/application/ViewStateComputation;)Lkotlinx/coroutines/flow/Flow;
}

public final class com/fraktalio/fmodel/application/MemoryViewExtensionKt {
public static final fun handle (Lcom/fraktalio/fmodel/application/ViewStateComputation;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/fraktalio/fmodel/application/SagaManagerActorExtensionKt {
public static final fun handleConcurrently (Lcom/fraktalio/fmodel/application/SagaManager;Lkotlinx/coroutines/flow/Flow;IILkotlinx/coroutines/CoroutineStart;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun handleConcurrently$default (Lcom/fraktalio/fmodel/application/SagaManager;Lkotlinx/coroutines/flow/Flow;IILkotlinx/coroutines/CoroutineStart;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.fraktalio.fmodel.application

import kotlinx.coroutines.flow.fold

/**
* Extension function - Handles the query of type [Q]
*
* @param query Query of type [Q] to be handled
* @return State of type [S]
*
* @author Domenic Cassisi
*/
suspend fun <S, E, Q, MV> MV.handle(query: Q): S where MV : ViewStateComputation<S, E>, MV : EphemeralViewRepository<E, Q> =
query.fetchEvents().fold(initialState) { s, e -> evolve(s, e) }

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.fraktalio.fmodel.application

import com.fraktalio.fmodel.application.examples.numbers.even.query.EvenNumberMemoryViewRepository
import com.fraktalio.fmodel.application.examples.numbers.even.query.evenNumberMemoryViewRepository
import com.fraktalio.fmodel.application.examples.numbers.even.query.EvenNumberEphemeralViewRepository
import com.fraktalio.fmodel.application.examples.numbers.even.query.evenNumberEphemeralViewRepository
import com.fraktalio.fmodel.domain.IView
import com.fraktalio.fmodel.domain.examples.numbers.api.Description
import com.fraktalio.fmodel.domain.examples.numbers.api.EvenNumberState
Expand All @@ -13,10 +13,10 @@ import io.kotest.matchers.shouldBe
/**
* DSL - Given
*/
private suspend fun <S, E, I> IView<S, E>.given(repository: MemoryViewRepository<E, I>, id: () -> I): S =
MemoryView(
private suspend fun <S, E, I> IView<S, E>.given(repository: EphemeralViewRepository<E, I>, id: () -> I): S =
EphemeralView(
view = this,
memoryViewRepository = repository
ephemeralViewRepository = repository
).handle(id())

/**
Expand All @@ -29,29 +29,29 @@ private fun <I> whenId(id: I): I = id
*/
private infix fun <S> S.thenState(expected: S) = shouldBe(expected)

class MemoryViewTest : FunSpec({
class EphemeralViewTest : FunSpec({
val evenView = evenNumberView()
val memoryViewRepository = evenNumberMemoryViewRepository() as EvenNumberMemoryViewRepository
val ephemeralViewRepository = evenNumberEphemeralViewRepository() as EvenNumberEphemeralViewRepository

test("Memory View - load number flow 1") {
test("Ephemeral View - load number flow 1") {
with(evenView) {
given(memoryViewRepository) {
given(ephemeralViewRepository) {
whenId(1)
} thenState EvenNumberState(Description("Initial state, Number 2, Number 4"), NumberValue(6))
}
}

test("Memory View - load number flow 2") {
test("Ephemeral View - load number flow 2") {
with(evenView) {
given(memoryViewRepository) {
given(ephemeralViewRepository) {
whenId(2)
} thenState EvenNumberState(Description("Initial state, Number 4, Number 2"), NumberValue(2))
}
}

test("Memory View - load non-existing number flow") {
test("Ephemeral View - load non-existing number flow") {
with(evenView) {
given(memoryViewRepository) {
given(ephemeralViewRepository) {
whenId(3)
} thenState EvenNumberState(Description("Initial state"), NumberValue(0))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.fraktalio.fmodel.application.examples.numbers.even.query

import com.fraktalio.fmodel.application.MemoryViewRepository
import com.fraktalio.fmodel.application.EphemeralViewRepository
import com.fraktalio.fmodel.domain.examples.numbers.api.Description
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberEvent
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberValue
Expand All @@ -22,9 +22,9 @@ private var numberFlow2 = flowOf(
)

/**
* Even number memory view implementation
* Even number ephemeral view implementation
*/
class EvenNumberMemoryViewRepository : MemoryViewRepository<NumberEvent.EvenNumberEvent?, Int> {
class EvenNumberEphemeralViewRepository : EphemeralViewRepository<NumberEvent.EvenNumberEvent?, Int> {

override fun Int.fetchEvents(): Flow<NumberEvent.EvenNumberEvent?> {
return when (this) {
Expand All @@ -37,7 +37,7 @@ class EvenNumberMemoryViewRepository : MemoryViewRepository<NumberEvent.EvenNumb
}

/**
* Helper function to create an [EvenNumberMemoryViewRepository]
* Helper function to create an [EvenNumberEphemeralViewRepository]
*/
fun evenNumberMemoryViewRepository(): MemoryViewRepository<NumberEvent.EvenNumberEvent?, Int> =
EvenNumberMemoryViewRepository()
fun evenNumberEphemeralViewRepository(): EphemeralViewRepository<NumberEvent.EvenNumberEvent?, Int> =
EvenNumberEphemeralViewRepository()
43 changes: 28 additions & 15 deletions application/api/application.api
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ public final class com/fraktalio/fmodel/application/ActionPublisher$DefaultImpls
public static fun publish (Lcom/fraktalio/fmodel/application/ActionPublisher;Lkotlinx/coroutines/flow/Flow;Ljava/util/Map;)Lkotlinx/coroutines/flow/Flow;
}

public abstract interface class com/fraktalio/fmodel/application/EphemeralView : com/fraktalio/fmodel/application/EphemeralViewRepository, com/fraktalio/fmodel/application/ViewStateComputation {
}

public final class com/fraktalio/fmodel/application/EphemeralView$DefaultImpls {
public static fun computeNewState (Lcom/fraktalio/fmodel/application/EphemeralView;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
}

public final class com/fraktalio/fmodel/application/EphemeralViewKt {
public static final fun EphemeralView (Lcom/fraktalio/fmodel/domain/IView;Lcom/fraktalio/fmodel/application/EphemeralViewRepository;)Lcom/fraktalio/fmodel/application/EphemeralView;
}

public abstract interface class com/fraktalio/fmodel/application/EphemeralViewRepository {
public abstract fun fetchEvents (Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
}

public abstract class com/fraktalio/fmodel/application/Error : com/fraktalio/fmodel/application/Result {
public abstract fun getThrowable ()Ljava/lang/Throwable;
}
Expand Down Expand Up @@ -110,6 +125,19 @@ public final class com/fraktalio/fmodel/application/Error$EventPublishingFailed
public fun toString ()Ljava/lang/String;
}

public final class com/fraktalio/fmodel/application/Error$FetchingEventsFailed : com/fraktalio/fmodel/application/Error {
public fun <init> (Ljava/lang/Object;Ljava/lang/Throwable;)V
public final fun component1 ()Ljava/lang/Object;
public final fun component2 ()Ljava/lang/Throwable;
public final fun copy (Ljava/lang/Object;Ljava/lang/Throwable;)Lcom/fraktalio/fmodel/application/Error$FetchingEventsFailed;
public static synthetic fun copy$default (Lcom/fraktalio/fmodel/application/Error$FetchingEventsFailed;Ljava/lang/Object;Ljava/lang/Throwable;ILjava/lang/Object;)Lcom/fraktalio/fmodel/application/Error$FetchingEventsFailed;
public fun equals (Ljava/lang/Object;)Z
public final fun getId ()Ljava/lang/Object;
public fun getThrowable ()Ljava/lang/Throwable;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/fraktalio/fmodel/application/Error$FetchingStateFailed : com/fraktalio/fmodel/application/Error {
public fun <init> (Ljava/lang/Object;Ljava/lang/Throwable;)V
public synthetic fun <init> (Ljava/lang/Object;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down Expand Up @@ -281,21 +309,6 @@ public final class com/fraktalio/fmodel/application/MaterializedViewKt {
public static final fun materializedView (Lcom/fraktalio/fmodel/domain/IView;Lcom/fraktalio/fmodel/application/ViewStateRepository;)Lcom/fraktalio/fmodel/application/MaterializedView;
}

public abstract interface class com/fraktalio/fmodel/application/MemoryView : com/fraktalio/fmodel/application/MemoryViewRepository, com/fraktalio/fmodel/application/ViewStateComputation {
}

public final class com/fraktalio/fmodel/application/MemoryView$DefaultImpls {
public static fun computeNewState (Lcom/fraktalio/fmodel/application/MemoryView;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
}

public final class com/fraktalio/fmodel/application/MemoryViewKt {
public static final fun MemoryView (Lcom/fraktalio/fmodel/domain/IView;Lcom/fraktalio/fmodel/application/MemoryViewRepository;)Lcom/fraktalio/fmodel/application/MemoryView;
}

public abstract interface class com/fraktalio/fmodel/application/MemoryViewRepository {
public abstract fun fetchEvents (Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
}

public abstract class com/fraktalio/fmodel/application/Result {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.fraktalio.fmodel.application

import com.fraktalio.fmodel.domain.IView

/**
* EphemeralView is using/delegating a `view` / [ViewStateComputation]<[S], [E]> to handle events of type [E] without maintaining a state of projection(s).
*
* [EphemeralView] extends [ViewStateComputation] and [EphemeralViewRepository] interfaces,
* clearly communicating that it is composed out of these two behaviours.
*
* @param S Ephemeral View state of type [S]
* @param E Events of type [E] that are handled by this Ephemeral View
* @param Q Query of type [Q]
*
* @author Domenic Cassisi
*/
interface EphemeralView<S, E, Q> : ViewStateComputation<S, E>, EphemeralViewRepository<E, Q>

/**
* Ephemeral View constructor-like function.
*
* The Delegation pattern has proven to be a good alternative to implementation inheritance, and Kotlin supports it natively requiring zero boilerplate code.
*
* @param S Ephemeral View state of type [S]
* @param E Events of type [E] that are used internally to build/fold new state
* @param Q Identifier of type [Q]
* @property view A view component of type [IView]<[S], [E]>
* @property ephemeralViewRepository Interface for fetching events for [Q] - dependencies by delegation
* @return An object/instance of type [EphemeralView]<[S], [E], [Q]>
*
* @author Domenic Cassisi
*/
fun <S, E, Q> EphemeralView(
view: IView<S, E>,
ephemeralViewRepository: EphemeralViewRepository<E, Q>
): EphemeralView<S, E, Q> =
object : EphemeralView<S, E, Q>,
EphemeralViewRepository<E, Q> by ephemeralViewRepository,
IView<S, E> by view {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ package com.fraktalio.fmodel.application
import kotlinx.coroutines.flow.Flow

/**
* Memory view repository interface
* Ephemeral view repository interface
*
* @param E Event
* @param I Identifier
* @param Q Query
*
* @author Domenic
* @author Domenic Cassisi
*/
fun interface MemoryViewRepository<E, I> {
fun interface EphemeralViewRepository<E, Q> {

/**
* Fetch events
*
* @receiver Identifier of type [I]
* @receiver Query of type [Q]
* @return the Flow of events of type [E]
*/
fun I.fetchEvents(): Flow<E>
fun Q.fetchEvents(): Flow<E>

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ sealed class Error : Result() {
) : Error()

data class StoringStateFailed<S>(val state: S, override val throwable: Throwable? = null) : Error()
data class FetchingEventsFailed<I>(val id: I, override val throwable: Throwable?): Error()
}


0 comments on commit e9f081e

Please sign in to comment.