Projection was now built on listener events

Create ProjectionBus and use it with listeners
add EventStream::getByVersion
This commit is contained in:
2025-03-18 21:48:37 +01:00
parent 908cc888ad
commit 8c1eabb9f5
22 changed files with 126 additions and 89 deletions

View File

@@ -4,7 +4,14 @@ import eventDemo.business.event.GameEventBus
import eventDemo.business.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.libs.bus.Bus import eventDemo.libs.bus.Bus
import eventDemo.libs.bus.BusInMemory import eventDemo.libs.bus.BusInMemory
import java.util.UUID
class GameEventBusInMemory : class GameEventBusInMemory :
GameEventBus(), GameEventBus,
Bus<GameEvent> by BusInMemory() Bus<GameEvent> by BusInMemory(),
Comparable<GameEventBusInMemory> {
private val instanceId: UUID = UUID.randomUUID()
override fun compareTo(other: GameEventBusInMemory): Int =
compareValues(instanceId, other.instanceId)
}

View File

@@ -1,8 +1,9 @@
package eventDemo.adapter.infrastructureLayer.event.projection package eventDemo.adapter.infrastructureLayer.event.projection
import eventDemo.business.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.business.event.GameEventHandler import eventDemo.business.event.GameEventBus
import eventDemo.business.event.GameEventStore import eventDemo.business.event.GameEventStore
import eventDemo.business.event.projection.GameProjectionBus
import eventDemo.business.event.projection.gameList.GameList import eventDemo.business.event.projection.gameList.GameList
import eventDemo.business.event.projection.gameList.GameListRepository import eventDemo.business.event.projection.gameList.GameListRepository
import eventDemo.business.event.projection.gameList.apply import eventDemo.business.event.projection.gameList.apply
@@ -10,9 +11,13 @@ import eventDemo.business.event.projection.gameState.GameState
import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory
import eventDemo.libs.event.projection.SnapshotConfig import eventDemo.libs.event.projection.SnapshotConfig
/**
* Manages [projections][GameList], their building and publication in the [bus][GameProjectionBus].
*/
class GameListRepositoryInMemory( class GameListRepositoryInMemory(
eventStore: GameEventStore, eventStore: GameEventStore,
eventHandler: GameEventHandler, projectionBus: GameProjectionBus,
eventBus: GameEventBus,
snapshotConfig: SnapshotConfig = SnapshotConfig(), snapshotConfig: SnapshotConfig = SnapshotConfig(),
) : GameListRepository { ) : GameListRepository {
private val projectionsSnapshot = private val projectionsSnapshot =
@@ -24,8 +29,10 @@ class GameListRepositoryInMemory(
) )
init { init {
eventHandler.registerProjectionBuilder { event -> eventBus.subscribe { event ->
projectionsSnapshot.applyAndPutToCache(event) projectionsSnapshot
.applyAndPutToCache(event)
.also { projectionBus.publish(it) }
} }
} }

View File

@@ -0,0 +1,18 @@
package eventDemo.adapter.infrastructureLayer.event.projection
import eventDemo.business.entity.GameId
import eventDemo.business.event.projection.GameProjectionBus
import eventDemo.libs.bus.Bus
import eventDemo.libs.bus.BusInMemory
import eventDemo.libs.event.projection.Projection
import java.util.UUID
class GameProjectionBusInMemory :
GameProjectionBus,
Bus<Projection<GameId>> by BusInMemory(),
Comparable<GameProjectionBusInMemory> {
private val instanceId: UUID = UUID.randomUUID()
override fun compareTo(other: GameProjectionBusInMemory): Int =
compareValues(instanceId, other.instanceId)
}

View File

@@ -1,18 +1,23 @@
package eventDemo.adapter.infrastructureLayer.event.projection package eventDemo.adapter.infrastructureLayer.event.projection
import eventDemo.business.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.business.event.GameEventHandler import eventDemo.business.event.GameEventBus
import eventDemo.business.event.GameEventStore import eventDemo.business.event.GameEventStore
import eventDemo.business.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.business.event.projection.GameProjectionBus
import eventDemo.business.event.projection.gameState.GameState import eventDemo.business.event.projection.gameState.GameState
import eventDemo.business.event.projection.gameState.GameStateRepository import eventDemo.business.event.projection.gameState.GameStateRepository
import eventDemo.business.event.projection.gameState.apply import eventDemo.business.event.projection.gameState.apply
import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory
import eventDemo.libs.event.projection.SnapshotConfig import eventDemo.libs.event.projection.SnapshotConfig
/**
* Manages [projections][GameState], their building and publication in the [bus][GameProjectionBus].
*/
class GameStateRepositoryInMemory( class GameStateRepositoryInMemory(
eventStore: GameEventStore, eventStore: GameEventStore,
eventHandler: GameEventHandler, projectionBus: GameProjectionBus,
eventBus: GameEventBus,
snapshotConfig: SnapshotConfig = SnapshotConfig(), snapshotConfig: SnapshotConfig = SnapshotConfig(),
) : GameStateRepository { ) : GameStateRepository {
private val projectionsSnapshot = private val projectionsSnapshot =
@@ -24,8 +29,11 @@ class GameStateRepositoryInMemory(
) )
init { init {
eventHandler.registerProjectionBuilder { event -> // On new event was received, build snapshot and publish it to the projection bus
projectionsSnapshot.applyAndPutToCache(event) eventBus.subscribe { event ->
projectionsSnapshot
.applyAndPutToCache(event)
.also { projectionBus.publish(it) }
} }
} }

View File

@@ -3,7 +3,7 @@ package eventDemo.adapter.interfaceLayer
import eventDemo.business.command.GameCommandHandler import eventDemo.business.command.GameCommandHandler
import eventDemo.business.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.business.entity.Player import eventDemo.business.entity.Player
import eventDemo.business.event.eventListener.PlayerNotificationEventListener import eventDemo.business.event.projection.projectionListener.PlayerNotificationListener
import eventDemo.business.notification.Notification import eventDemo.business.notification.Notification
import eventDemo.libs.fromFrameChannel import eventDemo.libs.fromFrameChannel
import eventDemo.libs.toObjectChannel import eventDemo.libs.toObjectChannel
@@ -24,7 +24,7 @@ import java.util.UUID
@DelicateCoroutinesApi @DelicateCoroutinesApi
fun Route.gameWebSocket( fun Route.gameWebSocket(
playerNotificationListener: PlayerNotificationEventListener, playerNotificationListener: PlayerNotificationListener,
commandHandler: GameCommandHandler, commandHandler: GameCommandHandler,
) { ) {
authenticate { authenticate {
@@ -43,7 +43,7 @@ fun Route.gameWebSocket(
private fun DefaultWebSocketServerSession.runWebSocket( private fun DefaultWebSocketServerSession.runWebSocket(
gameId: GameId, gameId: GameId,
commandHandler: GameCommandHandler, commandHandler: GameCommandHandler,
playerNotificationListener: PlayerNotificationEventListener, playerNotificationListener: PlayerNotificationListener,
) { ) {
val currentPlayer = call.getPlayer() val currentPlayer = call.getPlayer()
val outgoingFrameChannel: SendChannel<Notification> = fromFrameChannel(outgoing) val outgoingFrameChannel: SendChannel<Notification> = fromFrameChannel(outgoing)

View File

@@ -2,13 +2,5 @@ package eventDemo.business.event
import eventDemo.business.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.libs.bus.Bus import eventDemo.libs.bus.Bus
import java.util.UUID
abstract class GameEventBus : interface GameEventBus : Bus<GameEvent>
Bus<GameEvent>,
Comparable<GameEventBus> {
private val instanceId: UUID = UUID.randomUUID()
override fun compareTo(other: GameEventBus): Int =
compareValues(instanceId, other.instanceId)
}

View File

@@ -25,8 +25,7 @@ class GameEventHandler(
} }
/** /**
* Build Event, and send it to the event store and bus. * Build Event then send it to the event store and bus.
* Build also the projections.
*/ */
override fun handle( override fun handle(
aggregateId: GameId, aggregateId: GameId,
@@ -47,8 +46,6 @@ class GameEventHandler(
} }
}.also { event -> }.also { event ->
withLoggingContext("event" to event.toString()) { withLoggingContext("event" to event.toString()) {
// Build the projections
projectionsBuilders.forEach { it(event) }
// Publish to the bus // Publish to the bus
eventBus.publish(event) eventBus.publish(event)
} }

View File

@@ -0,0 +1,7 @@
package eventDemo.business.event.projection
import eventDemo.business.entity.GameId
import eventDemo.libs.bus.Bus
import eventDemo.libs.event.projection.Projection
interface GameProjectionBus : Bus<Projection<GameId>>

View File

@@ -5,6 +5,9 @@ import eventDemo.business.entity.Player
import eventDemo.libs.event.projection.Projection import eventDemo.libs.event.projection.Projection
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/**
* This [projection][Projection] is used to list all current games
*/
@Serializable @Serializable
data class GameList( data class GameList(
override val aggregateId: GameId, override val aggregateId: GameId,

View File

@@ -4,9 +4,13 @@ import eventDemo.business.entity.Card
import eventDemo.business.entity.Deck import eventDemo.business.entity.Deck
import eventDemo.business.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.business.entity.Player import eventDemo.business.entity.Player
import eventDemo.business.event.event.GameEvent
import eventDemo.libs.event.projection.Projection import eventDemo.libs.event.projection.Projection
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/**
* This [projection][Projection] is used for manage a game and theirs [card][Card]
*/
@Serializable @Serializable
data class GameState( data class GameState(
override val aggregateId: GameId, override val aggregateId: GameId,
@@ -20,6 +24,7 @@ data class GameState(
val deck: Deck = Deck(players), val deck: Deck = Deck(players),
val isStarted: Boolean = false, val isStarted: Boolean = false,
val playerWins: Set<Player> = emptySet(), val playerWins: Set<Player> = emptySet(),
val lastEvent: GameEvent? = null,
) : Projection<GameId> { ) : Projection<GameId> {
enum class Direction { enum class Direction {
CLOCKWISE, CLOCKWISE,

View File

@@ -111,5 +111,6 @@ fun GameState.apply(event: GameEvent): GameState =
} }
}.copy( }.copy(
lastEventVersion = event.version, lastEventVersion = event.version,
lastEvent = event,
) )
} }

View File

@@ -1,18 +1,17 @@
package eventDemo.business.event.eventListener package eventDemo.business.event.projection.projectionListener
import eventDemo.business.entity.Card import eventDemo.business.entity.Card
import eventDemo.business.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.business.entity.Player import eventDemo.business.entity.Player
import eventDemo.business.event.GameEventBus
import eventDemo.business.event.event.CardIsPlayedEvent import eventDemo.business.event.event.CardIsPlayedEvent
import eventDemo.business.event.event.GameEvent
import eventDemo.business.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.business.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.business.event.event.PlayerChoseColorEvent import eventDemo.business.event.event.PlayerChoseColorEvent
import eventDemo.business.event.event.PlayerHavePassEvent import eventDemo.business.event.event.PlayerHavePassEvent
import eventDemo.business.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.business.event.event.PlayerWinEvent import eventDemo.business.event.event.PlayerWinEvent
import eventDemo.business.event.projection.gameState.GameStateRepository import eventDemo.business.event.projection.GameProjectionBus
import eventDemo.business.event.projection.gameState.GameState
import eventDemo.business.notification.ItsTheTurnOfNotification import eventDemo.business.notification.ItsTheTurnOfNotification
import eventDemo.business.notification.Notification import eventDemo.business.notification.Notification
import eventDemo.business.notification.PlayerAsJoinTheGameNotification import eventDemo.business.notification.PlayerAsJoinTheGameNotification
@@ -27,9 +26,8 @@ import eventDemo.business.notification.YourNewCardNotification
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import io.github.oshai.kotlinlogging.withLoggingContext import io.github.oshai.kotlinlogging.withLoggingContext
class PlayerNotificationEventListener( class PlayerNotificationListener(
private val eventBus: GameEventBus, private val projectionBus: GameProjectionBus,
private val gameStateRepository: GameStateRepository,
) { ) {
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
@@ -38,14 +36,10 @@ class PlayerNotificationEventListener(
currentPlayer: Player, currentPlayer: Player,
gameId: GameId, gameId: GameId,
) { ) {
eventBus.subscribe { event: GameEvent -> projectionBus.subscribe { currentState ->
withLoggingContext("event" to event.toString()) { if (currentState !is GameState) return@subscribe
if (event.aggregateId != gameId) { if (currentState.aggregateId != gameId) return@subscribe
return@subscribe withLoggingContext("projection" to currentState.toString()) {
}
val currentState = gameStateRepository.getUntil(event)
fun Notification.send() { fun Notification.send() {
withLoggingContext("notification" to this.toString()) { withLoggingContext("notification" to this.toString()) {
if (currentState.players.contains(currentPlayer)) { if (currentState.players.contains(currentPlayer)) {
@@ -65,6 +59,10 @@ class PlayerNotificationEventListener(
player = currentState.currentPlayerTurn ?: error("No player turn defined"), player = currentState.currentPlayerTurn ?: error("No player turn defined"),
).send() ).send()
val event =
currentState.lastEvent
?: error("No last event in the GameState projection")
when (event) { when (event) {
is NewPlayerEvent -> { is NewPlayerEvent -> {
if (currentPlayer != event.player) { if (currentPlayer != event.player) {

View File

@@ -1,37 +1,35 @@
package eventDemo.business.event.eventListener package eventDemo.business.event.projection.projectionListener
import eventDemo.business.event.GameEventBus import eventDemo.business.entity.GameId
import eventDemo.business.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.business.event.event.GameEvent
import eventDemo.business.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.business.event.event.PlayerWinEvent import eventDemo.business.event.event.PlayerWinEvent
import eventDemo.business.event.projection.GameProjectionBus
import eventDemo.business.event.projection.gameState.GameState import eventDemo.business.event.projection.gameState.GameState
import eventDemo.business.event.projection.gameState.GameStateRepository import eventDemo.libs.event.projection.Projection
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import io.github.oshai.kotlinlogging.withLoggingContext import io.github.oshai.kotlinlogging.withLoggingContext
import java.util.concurrent.ConcurrentSkipListSet import java.util.concurrent.ConcurrentSkipListSet
class ReactionEventListener( class ReactionListener(
private val eventBus: GameEventBus, private val projectionBus: GameProjectionBus,
private val eventHandler: GameEventHandler, private val eventHandler: GameEventHandler,
private val gameStateRepository: GameStateRepository,
private val priority: Int = DEFAULT_PRIORITY, private val priority: Int = DEFAULT_PRIORITY,
) { ) {
companion object Config { companion object Config {
const val DEFAULT_PRIORITY = -1000 const val DEFAULT_PRIORITY = -1000
val registeredListeners = ConcurrentSkipListSet<GameEventBus>() val registeredListeners = ConcurrentSkipListSet<GameProjectionBus>()
} }
private val logger = KotlinLogging.logger { } private val logger = KotlinLogging.logger { }
fun init() { fun init() {
if (registeredListeners.add(eventBus)) { if (registeredListeners.add(projectionBus)) {
eventBus.subscribe(priority) { event: GameEvent -> projectionBus.subscribe(priority) { projection: Projection<GameId> ->
withLoggingContext("event" to event.toString()) { if (projection !is GameState) return@subscribe
val state = gameStateRepository.getUntil(event) withLoggingContext("projection" to projection.toString()) {
sendStartGameEvent(state, event) sendStartGameEvent(projection)
sendWinnerEvent(state) sendWinnerEvent(projection)
} }
} }
} else { } else {
@@ -39,10 +37,7 @@ class ReactionEventListener(
} }
} }
private fun sendStartGameEvent( private fun sendStartGameEvent(state: GameState) {
state: GameState,
event: GameEvent,
) {
if (state.isReady && !state.isStarted) { if (state.isReady && !state.isStarted) {
val reactionEvent = val reactionEvent =
eventHandler.handle(state.aggregateId) { eventHandler.handle(state.aggregateId) {
@@ -56,10 +51,6 @@ class ReactionEventListener(
message = "Reaction event was Send" message = "Reaction event was Send"
payload = mapOf("reactionEvent" to reactionEvent) payload = mapOf("reactionEvent" to reactionEvent)
} }
} else {
if (event is PlayerReadyEvent) {
logger.info { "All players was not ready ${state.readyPlayers}" }
}
} }
} }

View File

@@ -1,10 +1,10 @@
package eventDemo.configuration.business package eventDemo.configuration.business
import eventDemo.business.event.eventListener.ReactionEventListener import eventDemo.business.event.projection.projectionListener.ReactionListener
import io.ktor.server.application.Application import io.ktor.server.application.Application
import org.koin.ktor.ext.get import org.koin.ktor.ext.get
fun Application.configureGameListener() { fun Application.configureGameListener() {
ReactionEventListener(get(), get(), get()) ReactionListener(get(), get())
.init() .init()
} }

View File

@@ -3,7 +3,7 @@ package eventDemo.configuration.injection
import eventDemo.business.command.GameCommandActionRunner import eventDemo.business.command.GameCommandActionRunner
import eventDemo.business.command.GameCommandHandler import eventDemo.business.command.GameCommandHandler
import eventDemo.business.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.business.event.eventListener.PlayerNotificationEventListener import eventDemo.business.event.projection.projectionListener.PlayerNotificationListener
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf import org.koin.core.module.dsl.singleOf
@@ -13,5 +13,5 @@ fun Module.configureDIBusiness() {
} }
singleOf(::GameEventHandler) singleOf(::GameEventHandler)
singleOf(::GameCommandActionRunner) singleOf(::GameCommandActionRunner)
singleOf(::PlayerNotificationEventListener) singleOf(::PlayerNotificationListener)
} }

View File

@@ -3,29 +3,28 @@ package eventDemo.configuration.injection
import eventDemo.adapter.infrastructureLayer.event.GameEventBusInMemory import eventDemo.adapter.infrastructureLayer.event.GameEventBusInMemory
import eventDemo.adapter.infrastructureLayer.event.GameEventStoreInMemory import eventDemo.adapter.infrastructureLayer.event.GameEventStoreInMemory
import eventDemo.adapter.infrastructureLayer.event.projection.GameListRepositoryInMemory import eventDemo.adapter.infrastructureLayer.event.projection.GameListRepositoryInMemory
import eventDemo.adapter.infrastructureLayer.event.projection.GameProjectionBusInMemory
import eventDemo.adapter.infrastructureLayer.event.projection.GameStateRepositoryInMemory import eventDemo.adapter.infrastructureLayer.event.projection.GameStateRepositoryInMemory
import eventDemo.business.event.GameEventBus import eventDemo.business.event.GameEventBus
import eventDemo.business.event.GameEventStore import eventDemo.business.event.GameEventStore
import eventDemo.business.event.projection.GameProjectionBus
import eventDemo.business.event.projection.gameList.GameListRepository import eventDemo.business.event.projection.gameList.GameListRepository
import eventDemo.business.event.projection.gameState.GameStateRepository import eventDemo.business.event.projection.gameState.GameStateRepository
import eventDemo.libs.event.projection.SnapshotConfig import eventDemo.libs.event.projection.SnapshotConfig
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind import org.koin.dsl.bind
fun Module.configureDIInfrastructure() { fun Module.configureDIInfrastructure() {
single { singleOf(::GameEventBusInMemory) bind GameEventBus::class
GameEventBusInMemory() singleOf(::GameEventStoreInMemory) bind GameEventStore::class
} bind GameEventBus::class singleOf(::GameProjectionBusInMemory) bind GameProjectionBus::class
single { single {
GameEventStoreInMemory() GameStateRepositoryInMemory(get(), get(), get(), snapshotConfig = SnapshotConfig())
} bind GameEventStore::class
single {
GameStateRepositoryInMemory(get(), get(), snapshotConfig = SnapshotConfig())
} bind GameStateRepository::class } bind GameStateRepository::class
single { single {
GameListRepositoryInMemory(get(), get(), snapshotConfig = SnapshotConfig()) GameListRepositoryInMemory(get(), get(), get(), snapshotConfig = SnapshotConfig())
} bind GameListRepository::class } bind GameListRepository::class
} }

View File

@@ -2,14 +2,14 @@ package eventDemo.configuration.route
import eventDemo.adapter.interfaceLayer.gameWebSocket import eventDemo.adapter.interfaceLayer.gameWebSocket
import eventDemo.business.command.GameCommandHandler import eventDemo.business.command.GameCommandHandler
import eventDemo.business.event.eventListener.PlayerNotificationEventListener import eventDemo.business.event.projection.projectionListener.PlayerNotificationListener
import io.ktor.server.application.Application import io.ktor.server.application.Application
import io.ktor.server.routing.routing import io.ktor.server.routing.routing
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
fun Application.declareWebSocketsGameRoute( fun Application.declareWebSocketsGameRoute(
playerNotificationListener: PlayerNotificationEventListener, playerNotificationListener: PlayerNotificationListener,
commandHandler: GameCommandHandler, commandHandler: GameCommandHandler,
) { ) {
routing { routing {

View File

@@ -16,4 +16,6 @@ interface EventStream<E : Event<*>> {
fun readGreaterOfVersion(version: Int): Set<E> fun readGreaterOfVersion(version: Int): Set<E>
fun readVersionBetween(version: IntRange): Set<E> fun readVersionBetween(version: IntRange): Set<E>
fun getByVersion(version: Int): E?
} }

View File

@@ -41,4 +41,7 @@ class EventStreamInMemory<E : Event<*>> : EventStream<E> {
events events
.filter { version.contains(it.version) } .filter { version.contains(it.version) }
.toSet() .toSet()
override fun getByVersion(version: Int): E? =
events.find { version == it.version }
} }

View File

@@ -50,13 +50,12 @@ class ProjectionSnapshotRepositoryInMemory<E : Event<ID>, P : Projection<ID>, ID
* 5. save it * 5. save it
* 6. remove old one * 6. remove old one
*/ */
fun applyAndPutToCache(event: E) { fun applyAndPutToCache(event: E): P =
getUntil(event) getUntil(event)
.also { .also {
save(it) save(it)
removeOldSnapshot(it.aggregateId) removeOldSnapshot(it.aggregateId)
} }
}
/** /**
* Build the list of all [Projections][Projection] * Build the list of all [Projections][Projection]

View File

@@ -5,8 +5,8 @@ import eventDemo.business.command.command.GameCommand
import eventDemo.business.command.command.IWantToJoinTheGameCommand import eventDemo.business.command.command.IWantToJoinTheGameCommand
import eventDemo.business.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.business.entity.Player import eventDemo.business.entity.Player
import eventDemo.business.event.eventListener.PlayerNotificationEventListener import eventDemo.business.event.projection.projectionListener.PlayerNotificationListener
import eventDemo.business.event.eventListener.ReactionEventListener import eventDemo.business.event.projection.projectionListener.ReactionListener
import eventDemo.business.notification.CommandSuccessNotification import eventDemo.business.notification.CommandSuccessNotification
import eventDemo.business.notification.Notification import eventDemo.business.notification.Notification
import eventDemo.business.notification.WelcomeToTheGameNotification import eventDemo.business.notification.WelcomeToTheGameNotification
@@ -28,12 +28,12 @@ class GameCommandHandlerTest :
test("handle a command should execute the command") { test("handle a command should execute the command") {
koinApplication { modules(appKoinModule) }.koin.apply { koinApplication { modules(appKoinModule) }.koin.apply {
val commandHandler by inject<GameCommandHandler>() val commandHandler by inject<GameCommandHandler>()
val notificationListener by inject<PlayerNotificationEventListener>() val notificationListener by inject<PlayerNotificationListener>()
val gameId = GameId() val gameId = GameId()
val player = Player("Tesla") val player = Player("Tesla")
val channelCommand = Channel<GameCommand>(Channel.BUFFERED) val channelCommand = Channel<GameCommand>(Channel.BUFFERED)
val channelNotification = Channel<Notification>(Channel.BUFFERED) val channelNotification = Channel<Notification>(Channel.BUFFERED)
ReactionEventListener(get(), get(), get()).init() ReactionListener(get(), get()).init()
notificationListener.startListening( notificationListener.startListening(
{ channelNotification.trySendBlocking(it) }, { channelNotification.trySendBlocking(it) },
player, player,

View File

@@ -10,10 +10,10 @@ import eventDemo.business.entity.GameId
import eventDemo.business.entity.Player import eventDemo.business.entity.Player
import eventDemo.business.event.GameEventStore import eventDemo.business.event.GameEventStore
import eventDemo.business.event.event.disableShuffleDeck import eventDemo.business.event.event.disableShuffleDeck
import eventDemo.business.event.eventListener.PlayerNotificationEventListener
import eventDemo.business.event.eventListener.ReactionEventListener
import eventDemo.business.event.projection.gameState.GameState import eventDemo.business.event.projection.gameState.GameState
import eventDemo.business.event.projection.gameState.apply import eventDemo.business.event.projection.gameState.apply
import eventDemo.business.event.projection.projectionListener.PlayerNotificationListener
import eventDemo.business.event.projection.projectionListener.ReactionListener
import eventDemo.business.notification.CommandSuccessNotification import eventDemo.business.notification.CommandSuccessNotification
import eventDemo.business.notification.ItsTheTurnOfNotification import eventDemo.business.notification.ItsTheTurnOfNotification
import eventDemo.business.notification.Notification import eventDemo.business.notification.Notification
@@ -173,8 +173,8 @@ class GameSimulationTest :
koinApplication { modules(appKoinModule) }.koin.apply { koinApplication { modules(appKoinModule) }.koin.apply {
val commandHandler by inject<GameCommandHandler>() val commandHandler by inject<GameCommandHandler>()
val eventStore by inject<GameEventStore>() val eventStore by inject<GameEventStore>()
val playerNotificationListener by inject<PlayerNotificationEventListener>() val playerNotificationListener by inject<PlayerNotificationListener>()
ReactionEventListener(get(), get(), get()).init() ReactionListener(get(), get()).init()
playerNotificationListener.startListening({ channelNotification1.trySendBlocking(it) }, player1, gameId) playerNotificationListener.startListening({ channelNotification1.trySendBlocking(it) }, player1, gameId)
playerNotificationListener.startListening({ channelNotification2.trySendBlocking(it) }, player2, gameId) playerNotificationListener.startListening({ channelNotification2.trySendBlocking(it) }, player2, gameId)