Move file and add interface to improve Archi Hexa

This commit is contained in:
2025-03-16 02:47:08 +01:00
parent 4739495980
commit 769d104040
77 changed files with 388 additions and 337 deletions

View File

@@ -0,0 +1,11 @@
package eventDemo.adapter.infrastructureLayer.event
import eventDemo.business.entity.GameId
import eventDemo.business.event.GameEventBus
import eventDemo.business.event.event.GameEvent
import eventDemo.libs.event.EventBus
import eventDemo.libs.event.EventBusInMemory
class GameEventBusInMemory :
GameEventBus,
EventBus<GameEvent, GameId> by EventBusInMemory<GameEvent, GameId>()

View File

@@ -0,0 +1,14 @@
package eventDemo.adapter.infrastructureLayer.event
import eventDemo.business.entity.GameId
import eventDemo.business.event.GameEventStore
import eventDemo.business.event.event.GameEvent
import eventDemo.libs.event.EventStore
import eventDemo.libs.event.EventStoreInMemory
/**
* A stream to publish and read the played card event.
*/
class GameEventStoreInMemory :
GameEventStore,
EventStore<GameEvent, GameId> by EventStoreInMemory()

View File

@@ -1,15 +1,20 @@
package eventDemo.app.event.projection package eventDemo.adapter.infrastructureLayer.event.projection
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.app.event.GameEventStore import eventDemo.business.event.GameEventStore
import eventDemo.app.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.business.event.projection.GameState
import eventDemo.business.event.projection.GameStateRepository
import eventDemo.business.event.projection.apply
import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory
import eventDemo.libs.event.projection.SnapshotConfig
class GameStateRepository( class GameStateRepositoryInMemory(
eventStore: GameEventStore, eventStore: GameEventStore,
eventHandler: GameEventHandler, eventHandler: GameEventHandler,
snapshotConfig: SnapshotConfig = SnapshotConfig(), snapshotConfig: SnapshotConfig = SnapshotConfig(),
) { ) : GameStateRepository {
private val projectionsSnapshot = private val projectionsSnapshot =
ProjectionSnapshotRepositoryInMemory( ProjectionSnapshotRepositoryInMemory(
eventStore = eventStore, eventStore = eventStore,
@@ -29,7 +34,7 @@ class GameStateRepository(
* *
* It fetches it from the local cache if possible, otherwise it builds it. * It fetches it from the local cache if possible, otherwise it builds it.
*/ */
fun getLast(gameId: GameId): GameState = override fun getLast(gameId: GameId): GameState =
projectionsSnapshot.getLast(gameId) projectionsSnapshot.getLast(gameId)
/** /**
@@ -38,6 +43,6 @@ class GameStateRepository(
* *
* It fetches it from the local cache if possible, otherwise it builds it. * It fetches it from the local cache if possible, otherwise it builds it.
*/ */
fun getUntil(event: GameEvent): GameState = override fun getUntil(event: GameEvent): GameState =
projectionsSnapshot.getUntil(event) projectionsSnapshot.getUntil(event)
} }

View File

@@ -1,8 +1,9 @@
package eventDemo.app.command package eventDemo.adapter.interfaceLayer
import eventDemo.app.entity.Player import eventDemo.business.command.GameCommandHandler
import eventDemo.app.eventListener.PlayerNotificationEventListener import eventDemo.business.entity.Player
import eventDemo.app.notification.Notification import eventDemo.business.event.eventListener.PlayerNotificationEventListener
import eventDemo.business.notification.Notification
import eventDemo.libs.fromFrameChannel import eventDemo.libs.fromFrameChannel
import eventDemo.libs.toObjectChannel import eventDemo.libs.toObjectChannel
import io.github.oshai.kotlinlogging.withLoggingContext import io.github.oshai.kotlinlogging.withLoggingContext
@@ -15,6 +16,7 @@ import io.ktor.server.websocket.webSocket
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@DelicateCoroutinesApi @DelicateCoroutinesApi
@@ -34,7 +36,7 @@ fun Route.gameWebSocket(
outgoingFrameChannel, outgoingFrameChannel,
) )
} }
playerNotificationListener.startListening(outgoingFrameChannel, currentPlayer) playerNotificationListener.startListening({ outgoingFrameChannel.trySendBlocking(it) }, currentPlayer)
} }
} }
} }

View File

@@ -1,7 +1,7 @@
package eventDemo.app.query package eventDemo.adapter.interfaceLayer
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
import eventDemo.configuration.GameIdSerializer import eventDemo.configuration.GameIdSerializer
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.resources.Resource import io.ktor.resources.Resource

View File

@@ -1,27 +0,0 @@
package eventDemo.app.command
import eventDemo.app.command.action.ICantPlay
import eventDemo.app.command.action.IWantToJoinTheGame
import eventDemo.app.command.action.IWantToPlayCard
import eventDemo.app.command.action.IamReadyToPlay
import eventDemo.app.command.command.GameCommand
import eventDemo.app.command.command.ICantPlayCommand
import eventDemo.app.command.command.IWantToJoinTheGameCommand
import eventDemo.app.command.command.IWantToPlayCardCommand
import eventDemo.app.command.command.IamReadyToPlayCommand
import eventDemo.app.event.event.GameEvent
class GameCommandActionRunner(
private val iWantToPlayCard: IWantToPlayCard,
private val iamReadyToPlay: IamReadyToPlay,
private val iWantToJoinTheGame: IWantToJoinTheGame,
private val iCantPlay: ICantPlay,
) {
fun run(command: GameCommand): (Int) -> GameEvent =
when (command) {
is IWantToPlayCardCommand -> iWantToPlayCard.run(command)
is IamReadyToPlayCommand -> iamReadyToPlay.run(command)
is IWantToJoinTheGameCommand -> iWantToJoinTheGame.run(command)
is ICantPlayCommand -> iCantPlay.run(command)
}
}

View File

@@ -1,9 +0,0 @@
package eventDemo.app.event
import eventDemo.app.entity.GameId
import eventDemo.app.event.event.GameEvent
import eventDemo.libs.event.EventBus
class GameEventBus(
bus: EventBus<GameEvent, GameId>,
) : EventBus<GameEvent, GameId> by bus

View File

@@ -1,12 +0,0 @@
package eventDemo.app.event
import eventDemo.app.entity.GameId
import eventDemo.app.event.event.GameEvent
import eventDemo.libs.event.EventStore
/**
* A stream to publish and read the played card event.
*/
class GameEventStore(
private val eventStore: EventStore<GameEvent, GameId>,
) : EventStore<GameEvent, GameId> by eventStore

View File

@@ -1,7 +0,0 @@
package eventDemo.app.event.event
import eventDemo.app.entity.Player
sealed interface PlayerActionEvent : GameEvent {
val player: Player
}

View File

@@ -1,4 +1,4 @@
package eventDemo.app.command package eventDemo.business.command
class CommandException( class CommandException(
override val message: String, override val message: String,

View File

@@ -0,0 +1,27 @@
package eventDemo.business.command
import eventDemo.business.command.action.ICantPlay
import eventDemo.business.command.action.IWantToJoinTheGame
import eventDemo.business.command.action.IWantToPlayCard
import eventDemo.business.command.action.IamReadyToPlay
import eventDemo.business.command.command.GameCommand
import eventDemo.business.command.command.ICantPlayCommand
import eventDemo.business.command.command.IWantToJoinTheGameCommand
import eventDemo.business.command.command.IWantToPlayCardCommand
import eventDemo.business.command.command.IamReadyToPlayCommand
import eventDemo.business.event.event.GameEvent
class GameCommandActionRunner(
private val iWantToPlayCard: IWantToPlayCard,
private val iamReadyToPlay: IamReadyToPlay,
private val iWantToJoinTheGame: IWantToJoinTheGame,
private val iCantPlay: ICantPlay,
) {
fun run(command: GameCommand): (version: Int) -> GameEvent =
when (command) {
is IWantToPlayCardCommand -> iWantToPlayCard.run(command)
is IamReadyToPlayCommand -> iamReadyToPlay.run(command)
is IWantToJoinTheGameCommand -> iWantToJoinTheGame.run(command)
is ICantPlayCommand -> iCantPlay.run(command)
}
}

View File

@@ -1,13 +1,13 @@
package eventDemo.app.command package eventDemo.business.command
import eventDemo.app.command.command.GameCommand import eventDemo.business.command.command.GameCommand
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.event.GameEventBus import eventDemo.business.event.GameEventBus
import eventDemo.app.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.app.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.app.notification.CommandErrorNotification import eventDemo.business.notification.CommandErrorNotification
import eventDemo.app.notification.CommandSuccessNotification import eventDemo.business.notification.CommandSuccessNotification
import eventDemo.app.notification.Notification import eventDemo.business.notification.Notification
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import eventDemo.libs.command.CommandStreamChannel import eventDemo.libs.command.CommandStreamChannel
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
@@ -62,7 +62,7 @@ class GameCommandHandler(
withLoggingContext("command" to command.toString()) { withLoggingContext("command" to command.toString()) {
if (command.payload.player.id != player.id) { if (command.payload.player.id != player.id) {
logger.warn { "Handle command Refuse, the player of the command is not the same" } logger.warn { "Handle command Refuse, the player of the command is not the same" }
channelNotification.sendError(command)("You are not the author of this command\n") channelNotification.sendError(command)("You are not the author of this command")
} else { } else {
logger.info { "Handle command" } logger.info { "Handle command" }
try { try {
@@ -94,7 +94,7 @@ private fun SendChannel<Notification>.sendSuccess(commandId: CommandId): suspend
} }
} }
private fun SendChannel<Notification>.sendError(command: GameCommand): suspend (String) -> Unit = private fun SendChannel<Notification>.sendError(command: GameCommand): suspend (message: String) -> Unit =
{ {
val logger = KotlinLogging.logger { } val logger = KotlinLogging.logger { }
CommandErrorNotification(message = it, command = command) CommandErrorNotification(message = it, command = command)

View File

@@ -1,8 +1,8 @@
package eventDemo.app.command.action package eventDemo.business.command.action
import eventDemo.libs.command.Command import eventDemo.libs.command.Command
import eventDemo.libs.event.Event import eventDemo.libs.event.Event
sealed interface CommandAction<C : Command, E : Event<*>> { sealed interface CommandAction<C : Command, E : Event<*>> {
fun run(command: C): (Int) -> E fun run(command: C): (version: Int) -> E
} }

View File

@@ -1,9 +1,9 @@
package eventDemo.app.command.action package eventDemo.business.command.action
import eventDemo.app.command.CommandException import eventDemo.business.command.CommandException
import eventDemo.app.command.command.ICantPlayCommand import eventDemo.business.command.command.ICantPlayCommand
import eventDemo.app.event.event.PlayerHavePassEvent import eventDemo.business.event.event.PlayerHavePassEvent
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
/** /**
* A command to perform an action to play a new card * A command to perform an action to play a new card
@@ -11,7 +11,7 @@ import eventDemo.app.event.projection.GameStateRepository
data class ICantPlay( data class ICantPlay(
private val gameStateRepository: GameStateRepository, private val gameStateRepository: GameStateRepository,
) : CommandAction<ICantPlayCommand, PlayerHavePassEvent> { ) : CommandAction<ICantPlayCommand, PlayerHavePassEvent> {
override fun run(command: ICantPlayCommand): (Int) -> PlayerHavePassEvent { override fun run(command: ICantPlayCommand): (version: Int) -> PlayerHavePassEvent {
val state = gameStateRepository.getLast(command.payload.aggregateId) val state = gameStateRepository.getLast(command.payload.aggregateId)
if (state.currentPlayerTurn != command.payload.player) { if (state.currentPlayerTurn != command.payload.player) {

View File

@@ -1,9 +1,9 @@
package eventDemo.app.command.action package eventDemo.business.command.action
import eventDemo.app.command.CommandException import eventDemo.business.command.CommandException
import eventDemo.app.command.command.IWantToJoinTheGameCommand import eventDemo.business.command.command.IWantToJoinTheGameCommand
import eventDemo.app.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
/** /**
* A command to perform an action to play a new card * A command to perform an action to play a new card
@@ -11,7 +11,7 @@ import eventDemo.app.event.projection.GameStateRepository
data class IWantToJoinTheGame( data class IWantToJoinTheGame(
private val gameStateRepository: GameStateRepository, private val gameStateRepository: GameStateRepository,
) : CommandAction<IWantToJoinTheGameCommand, NewPlayerEvent> { ) : CommandAction<IWantToJoinTheGameCommand, NewPlayerEvent> {
override fun run(command: IWantToJoinTheGameCommand): (Int) -> NewPlayerEvent { override fun run(command: IWantToJoinTheGameCommand): (version: Int) -> NewPlayerEvent {
val state = gameStateRepository.getLast(command.payload.aggregateId) val state = gameStateRepository.getLast(command.payload.aggregateId)
if (!state.isStarted) { if (!state.isStarted) {
return { return {

View File

@@ -1,9 +1,9 @@
package eventDemo.app.command.action package eventDemo.business.command.action
import eventDemo.app.command.CommandException import eventDemo.business.command.CommandException
import eventDemo.app.command.command.IWantToPlayCardCommand import eventDemo.business.command.command.IWantToPlayCardCommand
import eventDemo.app.event.event.CardIsPlayedEvent import eventDemo.business.event.event.CardIsPlayedEvent
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
/** /**
* A command to perform an action to play a new card * A command to perform an action to play a new card
@@ -11,7 +11,7 @@ import eventDemo.app.event.projection.GameStateRepository
data class IWantToPlayCard( data class IWantToPlayCard(
private val gameStateRepository: GameStateRepository, private val gameStateRepository: GameStateRepository,
) : CommandAction<IWantToPlayCardCommand, CardIsPlayedEvent> { ) : CommandAction<IWantToPlayCardCommand, CardIsPlayedEvent> {
override fun run(command: IWantToPlayCardCommand): (Int) -> CardIsPlayedEvent { override fun run(command: IWantToPlayCardCommand): (version: Int) -> CardIsPlayedEvent {
val state = gameStateRepository.getLast(command.payload.aggregateId) val state = gameStateRepository.getLast(command.payload.aggregateId)
if (!state.isStarted) { if (!state.isStarted) {

View File

@@ -1,9 +1,9 @@
package eventDemo.app.command.action package eventDemo.business.command.action
import eventDemo.app.command.CommandException import eventDemo.business.command.CommandException
import eventDemo.app.command.command.IamReadyToPlayCommand import eventDemo.business.command.command.IamReadyToPlayCommand
import eventDemo.app.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
/** /**
* A command to set as ready to play * A command to set as ready to play
@@ -12,7 +12,7 @@ class IamReadyToPlay(
private val gameStateRepository: GameStateRepository, private val gameStateRepository: GameStateRepository,
) : CommandAction<IamReadyToPlayCommand, PlayerReadyEvent> { ) : CommandAction<IamReadyToPlayCommand, PlayerReadyEvent> {
@Throws(CommandException::class) @Throws(CommandException::class)
override fun run(command: IamReadyToPlayCommand): (Int) -> PlayerReadyEvent { override fun run(command: IamReadyToPlayCommand): (version: Int) -> PlayerReadyEvent {
val state = gameStateRepository.getLast(command.payload.aggregateId) val state = gameStateRepository.getLast(command.payload.aggregateId)
val playerExist: Boolean = state.players.contains(command.payload.player) val playerExist: Boolean = state.players.contains(command.payload.player)
val playerIsAlreadyReady: Boolean = state.readyPlayers.contains(command.payload.player) val playerIsAlreadyReady: Boolean = state.readyPlayers.contains(command.payload.player)

View File

@@ -1,7 +1,7 @@
package eventDemo.app.command.command package eventDemo.business.command.command
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.libs.command.Command import eventDemo.libs.command.Command
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,7 +1,7 @@
package eventDemo.app.command.command package eventDemo.business.command.command
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,7 +1,7 @@
package eventDemo.app.command.command package eventDemo.business.command.command
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,8 +1,8 @@
package eventDemo.app.command.command package eventDemo.business.command.command
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,7 +1,7 @@
package eventDemo.app.command.command package eventDemo.business.command.command
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package eventDemo.app.entity package eventDemo.business.entity
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName

View File

@@ -1,4 +1,4 @@
package eventDemo.app.entity package eventDemo.business.entity
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package eventDemo.app.entity package eventDemo.business.entity
import eventDemo.configuration.GameIdSerializer import eventDemo.configuration.GameIdSerializer
import eventDemo.libs.event.AggregateId import eventDemo.libs.event.AggregateId

View File

@@ -1,4 +1,4 @@
package eventDemo.app.entity package eventDemo.business.entity
import eventDemo.configuration.PlayerIdSerializer import eventDemo.configuration.PlayerIdSerializer
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer

View File

@@ -1,4 +1,4 @@
package eventDemo.app.entity package eventDemo.business.entity
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package eventDemo.app.event package eventDemo.business.event
import eventDemo.libs.event.AggregateId import eventDemo.libs.event.AggregateId
import eventDemo.libs.event.Event import eventDemo.libs.event.Event
@@ -7,7 +7,7 @@ import eventDemo.libs.event.Event
* A stream to publish and read the played card event. * A stream to publish and read the played card event.
*/ */
interface EventHandler<E : Event<ID>, ID : AggregateId> { interface EventHandler<E : Event<ID>, ID : AggregateId> {
fun registerProjectionBuilder(builder: (E) -> Unit) fun registerProjectionBuilder(builder: (event: E) -> Unit)
fun handle( fun handle(
aggregateId: ID, aggregateId: ID,

View File

@@ -0,0 +1,7 @@
package eventDemo.business.event
import eventDemo.business.entity.GameId
import eventDemo.business.event.event.GameEvent
import eventDemo.libs.event.EventBus
interface GameEventBus : EventBus<GameEvent, GameId>

View File

@@ -1,7 +1,7 @@
package eventDemo.app.event package eventDemo.business.event
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.libs.event.VersionBuilder import eventDemo.libs.event.VersionBuilder
import io.github.oshai.kotlinlogging.withLoggingContext import io.github.oshai.kotlinlogging.withLoggingContext
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@@ -20,7 +20,7 @@ class GameEventHandler(
private val projectionsBuilders: ConcurrentLinkedQueue<(GameEvent) -> Unit> = ConcurrentLinkedQueue() private val projectionsBuilders: ConcurrentLinkedQueue<(GameEvent) -> Unit> = ConcurrentLinkedQueue()
private val locks: ConcurrentHashMap<GameId, ReentrantLock> = ConcurrentHashMap() private val locks: ConcurrentHashMap<GameId, ReentrantLock> = ConcurrentHashMap()
override fun registerProjectionBuilder(builder: GameProjectionBuilder) { override fun registerProjectionBuilder(builder: (event: GameEvent) -> Unit) {
projectionsBuilders.add(builder) projectionsBuilders.add(builder)
} }
@@ -55,5 +55,3 @@ class GameEventHandler(
} }
} }
} }
typealias GameProjectionBuilder = (GameEvent) -> Unit

View File

@@ -0,0 +1,7 @@
package eventDemo.business.event
import eventDemo.business.entity.GameId
import eventDemo.business.event.event.GameEvent
import eventDemo.libs.event.EventStore
interface GameEventStore : EventStore<GameEvent, GameId>

View File

@@ -1,8 +1,8 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.libs.event.Event import eventDemo.libs.event.Event
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,9 +1,9 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.Deck import eventDemo.business.entity.Deck
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.entity.initHands import eventDemo.business.entity.initHands
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -1,7 +1,7 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -0,0 +1,7 @@
package eventDemo.business.event.event
import eventDemo.business.entity.Player
sealed interface PlayerActionEvent : GameEvent {
val player: Player
}

View File

@@ -1,8 +1,8 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -1,8 +1,8 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -1,7 +1,7 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -1,7 +1,7 @@
package eventDemo.app.event.event package eventDemo.business.event.event
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import java.util.UUID import java.util.UUID

View File

@@ -1,32 +1,30 @@
package eventDemo.app.eventListener package eventDemo.business.event.eventListener
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.event.GameEventBus import eventDemo.business.event.GameEventBus
import eventDemo.app.event.event.CardIsPlayedEvent import eventDemo.business.event.event.CardIsPlayedEvent
import eventDemo.app.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.app.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.app.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.app.event.event.PlayerChoseColorEvent import eventDemo.business.event.event.PlayerChoseColorEvent
import eventDemo.app.event.event.PlayerHavePassEvent import eventDemo.business.event.event.PlayerHavePassEvent
import eventDemo.app.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.app.event.event.PlayerWinEvent import eventDemo.business.event.event.PlayerWinEvent
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
import eventDemo.app.notification.ItsTheTurnOfNotification import eventDemo.business.notification.ItsTheTurnOfNotification
import eventDemo.app.notification.Notification import eventDemo.business.notification.Notification
import eventDemo.app.notification.PlayerAsJoinTheGameNotification import eventDemo.business.notification.PlayerAsJoinTheGameNotification
import eventDemo.app.notification.PlayerAsPlayACardNotification import eventDemo.business.notification.PlayerAsPlayACardNotification
import eventDemo.app.notification.PlayerHavePassNotification import eventDemo.business.notification.PlayerHavePassNotification
import eventDemo.app.notification.PlayerWasChoseTheCardColorNotification import eventDemo.business.notification.PlayerWasChoseTheCardColorNotification
import eventDemo.app.notification.PlayerWasReadyNotification import eventDemo.business.notification.PlayerWasReadyNotification
import eventDemo.app.notification.PlayerWinNotification import eventDemo.business.notification.PlayerWinNotification
import eventDemo.app.notification.TheGameWasStartedNotification import eventDemo.business.notification.TheGameWasStartedNotification
import eventDemo.app.notification.WelcomeToTheGameNotification import eventDemo.business.notification.WelcomeToTheGameNotification
import eventDemo.app.notification.YourNewCardNotification 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
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.trySendBlocking
class PlayerNotificationEventListener( class PlayerNotificationEventListener(
private val eventBus: GameEventBus, private val eventBus: GameEventBus,
@@ -35,7 +33,7 @@ class PlayerNotificationEventListener(
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
fun startListening( fun startListening(
outgoingNotificationChannel: SendChannel<Notification>, outgoingNotification: (Notification) -> Unit,
currentPlayer: Player, currentPlayer: Player,
) { ) {
eventBus.subscribe { event: GameEvent -> eventBus.subscribe { event: GameEvent ->
@@ -46,7 +44,7 @@ class PlayerNotificationEventListener(
withLoggingContext("notification" to this.toString()) { withLoggingContext("notification" to this.toString()) {
if (currentState.players.contains(currentPlayer)) { if (currentState.players.contains(currentPlayer)) {
// Only notify players who have already joined the game. // Only notify players who have already joined the game.
outgoingNotificationChannel.trySendBlocking(this) outgoingNotification(this)
logger.info { "Notification was SEND" } logger.info { "Notification was SEND" }
} else { } else {
// Rare use case, when a connexion is created with the channel, // Rare use case, when a connexion is created with the channel,

View File

@@ -1,13 +1,13 @@
package eventDemo.app.eventListener package eventDemo.business.event.eventListener
import eventDemo.app.event.GameEventBus import eventDemo.business.event.GameEventBus
import eventDemo.app.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.app.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.app.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.app.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.app.event.event.PlayerWinEvent import eventDemo.business.event.event.PlayerWinEvent
import eventDemo.app.event.projection.GameState import eventDemo.business.event.projection.GameState
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import io.github.oshai.kotlinlogging.withLoggingContext import io.github.oshai.kotlinlogging.withLoggingContext

View File

@@ -1,9 +1,9 @@
package eventDemo.app.event.projection package eventDemo.business.event.projection
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.Deck import eventDemo.business.entity.Deck
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable

View File

@@ -1,15 +1,15 @@
package eventDemo.app.event.projection package eventDemo.business.event.projection
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.event.event.CardIsPlayedEvent import eventDemo.business.event.event.CardIsPlayedEvent
import eventDemo.app.event.event.GameEvent import eventDemo.business.event.event.GameEvent
import eventDemo.app.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.app.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.app.event.event.PlayerActionEvent import eventDemo.business.event.event.PlayerActionEvent
import eventDemo.app.event.event.PlayerChoseColorEvent import eventDemo.business.event.event.PlayerChoseColorEvent
import eventDemo.app.event.event.PlayerHavePassEvent import eventDemo.business.event.event.PlayerHavePassEvent
import eventDemo.app.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.app.event.event.PlayerWinEvent import eventDemo.business.event.event.PlayerWinEvent
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
fun GameState.apply(event: GameEvent): GameState = fun GameState.apply(event: GameEvent): GameState =

View File

@@ -0,0 +1,10 @@
package eventDemo.business.event.projection
import eventDemo.business.entity.GameId
import eventDemo.business.event.event.GameEvent
interface GameStateRepository {
fun getLast(gameId: GameId): GameState
fun getUntil(event: GameEvent): GameState
}

View File

@@ -1,4 +1,4 @@
package eventDemo.app.event.projection package eventDemo.business.event.projection
import eventDemo.libs.event.AggregateId import eventDemo.libs.event.AggregateId

View File

@@ -1,4 +1,4 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import eventDemo.libs.command.Command import eventDemo.libs.command.Command

View File

@@ -1,3 +1,3 @@
package eventDemo.app.notification package eventDemo.business.notification
sealed interface CommandNotification : Notification sealed interface CommandNotification : Notification

View File

@@ -1,4 +1,4 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,4 +1,4 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,7 +1,7 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,7 +1,7 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -1,6 +1,6 @@
package eventDemo.app.notification package eventDemo.business.notification
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.configuration.UUIDSerializer import eventDemo.configuration.UUIDSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID

View File

@@ -2,7 +2,7 @@ package eventDemo.configuration
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application import io.ktor.server.application.Application
import io.ktor.server.application.call import io.ktor.server.application.call

View File

@@ -1,20 +1,21 @@
package eventDemo.configuration package eventDemo.configuration
import eventDemo.app.command.GameCommandActionRunner import eventDemo.adapter.infrastructureLayer.event.GameEventBusInMemory
import eventDemo.app.command.GameCommandHandler import eventDemo.adapter.infrastructureLayer.event.GameEventStoreInMemory
import eventDemo.app.command.command.GameCommand import eventDemo.adapter.infrastructureLayer.event.projection.GameStateRepositoryInMemory
import eventDemo.app.event.GameEventBus import eventDemo.business.command.GameCommandActionRunner
import eventDemo.app.event.GameEventHandler import eventDemo.business.command.GameCommandHandler
import eventDemo.app.event.GameEventStore import eventDemo.business.command.command.GameCommand
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.GameEventBus
import eventDemo.app.event.projection.SnapshotConfig import eventDemo.business.event.GameEventHandler
import eventDemo.app.eventListener.PlayerNotificationEventListener import eventDemo.business.event.GameEventStore
import eventDemo.business.event.eventListener.PlayerNotificationEventListener
import eventDemo.business.event.projection.GameStateRepository
import eventDemo.libs.command.CommandRunnerController import eventDemo.libs.command.CommandRunnerController
import eventDemo.libs.command.CommandStreamChannel import eventDemo.libs.command.CommandStreamChannel
import eventDemo.libs.event.EventBusInMemory
import eventDemo.libs.event.EventStoreInMemory
import eventDemo.libs.event.VersionBuilder import eventDemo.libs.event.VersionBuilder
import eventDemo.libs.event.VersionBuilderLocal import eventDemo.libs.event.VersionBuilderLocal
import eventDemo.libs.event.projection.SnapshotConfig
import io.ktor.server.application.Application import io.ktor.server.application.Application
import io.ktor.server.application.install import io.ktor.server.application.install
import org.koin.core.module.dsl.singleOf import org.koin.core.module.dsl.singleOf
@@ -33,14 +34,17 @@ fun Application.configureKoin() {
val appKoinModule = val appKoinModule =
module { module {
single { single {
GameEventBus(EventBusInMemory()) GameEventBusInMemory()
} } bind GameEventBus::class
single { single {
GameEventStore(EventStoreInMemory()) GameEventStoreInMemory()
} } bind GameEventStore::class
single { single {
GameStateRepository(get(), get(), snapshotConfig = SnapshotConfig()) GameStateRepositoryInMemory(get(), get(), snapshotConfig = SnapshotConfig())
} } bind GameStateRepository::class
single { single {
CommandStreamChannel<GameCommand>(get()) CommandStreamChannel<GameCommand>(get())
} }

View File

@@ -1,9 +1,9 @@
package eventDemo.configuration package eventDemo.configuration
import eventDemo.app.command.action.ICantPlay import eventDemo.business.command.action.ICantPlay
import eventDemo.app.command.action.IWantToJoinTheGame import eventDemo.business.command.action.IWantToJoinTheGame
import eventDemo.app.command.action.IWantToPlayCard import eventDemo.business.command.action.IWantToPlayCard
import eventDemo.app.command.action.IamReadyToPlay import eventDemo.business.command.action.IamReadyToPlay
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

View File

@@ -1,6 +1,6 @@
package eventDemo.configuration package eventDemo.configuration
import eventDemo.app.eventListener.ReactionEventListener import eventDemo.business.event.eventListener.ReactionEventListener
import io.ktor.server.application.Application import io.ktor.server.application.Application
import org.koin.ktor.ext.get import org.koin.ktor.ext.get

View File

@@ -1,7 +1,7 @@
package eventDemo.configuration package eventDemo.configuration
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import io.ktor.serialization.kotlinx.json.json import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application import io.ktor.server.application.Application

View File

@@ -1,6 +1,6 @@
package eventDemo.configuration package eventDemo.configuration
import eventDemo.app.query.readTheGameState import eventDemo.adapter.interfaceLayer.readTheGameState
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 org.koin.ktor.ext.get import org.koin.ktor.ext.get

View File

@@ -1,8 +1,8 @@
package eventDemo.configuration package eventDemo.configuration
import eventDemo.app.command.GameCommandHandler import eventDemo.adapter.interfaceLayer.gameWebSocket
import eventDemo.app.command.gameWebSocket import eventDemo.business.command.GameCommandHandler
import eventDemo.app.eventListener.PlayerNotificationEventListener import eventDemo.business.event.eventListener.PlayerNotificationEventListener
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

View File

@@ -1,5 +1,6 @@
package eventDemo.app.event.projection package eventDemo.libs.event.projection
import eventDemo.business.event.projection.Projection
import eventDemo.libs.event.AggregateId import eventDemo.libs.event.AggregateId
import eventDemo.libs.event.Event import eventDemo.libs.event.Event
import eventDemo.libs.event.EventStore import eventDemo.libs.event.EventStore
@@ -24,7 +25,7 @@ data class SnapshotConfig(
class ProjectionSnapshotRepositoryInMemory<E : Event<ID>, P : Projection<ID>, ID : AggregateId>( class ProjectionSnapshotRepositoryInMemory<E : Event<ID>, P : Projection<ID>, ID : AggregateId>(
private val eventStore: EventStore<E, ID>, private val eventStore: EventStore<E, ID>,
private val initialStateBuilder: (ID) -> P, private val initialStateBuilder: (aggregateId: ID) -> P,
private val snapshotCacheConfig: SnapshotConfig = SnapshotConfig(), private val snapshotCacheConfig: SnapshotConfig = SnapshotConfig(),
private val applyToProjection: P.(event: E) -> P, private val applyToProjection: P.(event: E) -> P,
) { ) {

View File

@@ -1,7 +1,7 @@
package eventDemo package eventDemo
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.Deck import eventDemo.business.entity.Deck
fun Deck.allCardCount(): Int = fun Deck.allCardCount(): Int =
stack.size + discard.size + playersHands.values.flatten().size stack.size + discard.size + playersHands.values.flatten().size

View File

@@ -1,14 +1,15 @@
package eventDemo.app.command package eventDemo.app.command
import eventDemo.app.command.command.GameCommand import eventDemo.business.command.GameCommandHandler
import eventDemo.app.command.command.IWantToJoinTheGameCommand import eventDemo.business.command.command.GameCommand
import eventDemo.app.entity.GameId import eventDemo.business.command.command.IWantToJoinTheGameCommand
import eventDemo.app.entity.Player import eventDemo.business.entity.GameId
import eventDemo.app.eventListener.PlayerNotificationEventListener import eventDemo.business.entity.Player
import eventDemo.app.eventListener.ReactionEventListener import eventDemo.business.event.eventListener.PlayerNotificationEventListener
import eventDemo.app.notification.CommandSuccessNotification import eventDemo.business.event.eventListener.ReactionEventListener
import eventDemo.app.notification.Notification import eventDemo.business.notification.CommandSuccessNotification
import eventDemo.app.notification.WelcomeToTheGameNotification import eventDemo.business.notification.Notification
import eventDemo.business.notification.WelcomeToTheGameNotification
import eventDemo.configuration.appKoinModule import eventDemo.configuration.appKoinModule
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContain
@@ -16,6 +17,7 @@ import io.kotest.matchers.equals.shouldBeEqual
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.dsl.koinApplication import org.koin.dsl.koinApplication
import kotlin.test.assertIs import kotlin.test.assertIs
@@ -32,7 +34,7 @@ class GameCommandHandlerTest :
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() ReactionEventListener(get(), get(), get()).init()
notificationListener.startListening(channelNotification, player) notificationListener.startListening({ channelNotification.trySendBlocking(it) }, player)
GlobalScope.launch { GlobalScope.launch {
commandHandler.handle(player, channelCommand, channelNotification) commandHandler.handle(player, channelCommand, channelNotification)

View File

@@ -2,6 +2,9 @@ package eventDemo.app.entity
import eventDemo.allCardCount import eventDemo.allCardCount
import eventDemo.allCards import eventDemo.allCards
import eventDemo.business.entity.Deck
import eventDemo.business.entity.Player
import eventDemo.business.entity.initHands
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeUnique import io.kotest.matchers.collections.shouldBeUnique
import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly

View File

@@ -1,5 +1,8 @@
package eventDemo.app.entity package eventDemo.app.entity
import eventDemo.business.entity.Card
import eventDemo.business.entity.Player
import eventDemo.business.entity.PlayersHands
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly
import kotlin.test.assertNotNull import kotlin.test.assertNotNull

View File

@@ -1,13 +1,15 @@
package eventDemo.app.event.projection package eventDemo.app.event.projection
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.event.event.CardIsPlayedEvent import eventDemo.business.event.event.CardIsPlayedEvent
import eventDemo.app.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.app.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.app.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.app.event.event.disableShuffleDeck import eventDemo.business.event.event.disableShuffleDeck
import eventDemo.business.event.projection.GameState
import eventDemo.business.event.projection.apply
import eventDemo.libs.event.VersionBuilderLocal import eventDemo.libs.event.VersionBuilderLocal
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual import io.kotest.matchers.equals.shouldBeEqual

View File

@@ -1,9 +1,10 @@
package eventDemo.app.event.projection package eventDemo.app.event.projection
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.app.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.business.event.projection.GameStateRepository
import eventDemo.configuration.appKoinModule import eventDemo.configuration.appKoinModule
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldHaveSize

View File

@@ -1,10 +1,13 @@
package eventDemo.app.event.projection package eventDemo.app.event.projection
import eventDemo.business.event.projection.Projection
import eventDemo.libs.event.AggregateId import eventDemo.libs.event.AggregateId
import eventDemo.libs.event.Event import eventDemo.libs.event.Event
import eventDemo.libs.event.EventStore import eventDemo.libs.event.EventStore
import eventDemo.libs.event.EventStoreInMemory import eventDemo.libs.event.EventStoreInMemory
import eventDemo.libs.event.VersionBuilderLocal import eventDemo.libs.event.VersionBuilderLocal
import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory
import eventDemo.libs.event.projection.SnapshotConfig
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual import io.kotest.matchers.equals.shouldBeEqual
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi

View File

@@ -1,15 +1,15 @@
package eventDemo.app.query package eventDemo.app.query
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.event.GameEventHandler import eventDemo.business.event.GameEventHandler
import eventDemo.app.event.event.CardIsPlayedEvent import eventDemo.business.event.event.CardIsPlayedEvent
import eventDemo.app.event.event.GameStartedEvent import eventDemo.business.event.event.GameStartedEvent
import eventDemo.app.event.event.NewPlayerEvent import eventDemo.business.event.event.NewPlayerEvent
import eventDemo.app.event.event.PlayerReadyEvent import eventDemo.business.event.event.PlayerReadyEvent
import eventDemo.app.event.projection.GameState import eventDemo.business.event.projection.GameState
import eventDemo.app.event.projection.GameStateRepository import eventDemo.business.event.projection.GameStateRepository
import eventDemo.configuration.configure import eventDemo.configuration.configure
import eventDemo.configuration.makeJwt import eventDemo.configuration.makeJwt
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec

View File

@@ -1,29 +1,29 @@
package eventDemo.app.query package eventDemo.app.query
import eventDemo.app.command.GameCommandHandler import eventDemo.business.command.GameCommandHandler
import eventDemo.app.command.command.GameCommand import eventDemo.business.command.command.GameCommand
import eventDemo.app.command.command.IWantToJoinTheGameCommand import eventDemo.business.command.command.IWantToJoinTheGameCommand
import eventDemo.app.command.command.IWantToPlayCardCommand import eventDemo.business.command.command.IWantToPlayCardCommand
import eventDemo.app.command.command.IamReadyToPlayCommand import eventDemo.business.command.command.IamReadyToPlayCommand
import eventDemo.app.entity.Card import eventDemo.business.entity.Card
import eventDemo.app.entity.GameId import eventDemo.business.entity.GameId
import eventDemo.app.entity.Player import eventDemo.business.entity.Player
import eventDemo.app.event.GameEventStore import eventDemo.business.event.GameEventStore
import eventDemo.app.event.event.disableShuffleDeck import eventDemo.business.event.event.disableShuffleDeck
import eventDemo.app.event.projection.GameState import eventDemo.business.event.eventListener.PlayerNotificationEventListener
import eventDemo.app.event.projection.ProjectionSnapshotRepositoryInMemory import eventDemo.business.event.eventListener.ReactionEventListener
import eventDemo.app.event.projection.apply import eventDemo.business.event.projection.GameState
import eventDemo.app.eventListener.PlayerNotificationEventListener import eventDemo.business.event.projection.apply
import eventDemo.app.eventListener.ReactionEventListener import eventDemo.business.notification.CommandSuccessNotification
import eventDemo.app.notification.CommandSuccessNotification import eventDemo.business.notification.ItsTheTurnOfNotification
import eventDemo.app.notification.ItsTheTurnOfNotification import eventDemo.business.notification.Notification
import eventDemo.app.notification.Notification import eventDemo.business.notification.PlayerAsJoinTheGameNotification
import eventDemo.app.notification.PlayerAsJoinTheGameNotification import eventDemo.business.notification.PlayerAsPlayACardNotification
import eventDemo.app.notification.PlayerAsPlayACardNotification import eventDemo.business.notification.PlayerWasReadyNotification
import eventDemo.app.notification.PlayerWasReadyNotification import eventDemo.business.notification.TheGameWasStartedNotification
import eventDemo.app.notification.TheGameWasStartedNotification import eventDemo.business.notification.WelcomeToTheGameNotification
import eventDemo.app.notification.WelcomeToTheGameNotification
import eventDemo.configuration.appKoinModule import eventDemo.configuration.appKoinModule
import eventDemo.libs.event.projection.ProjectionSnapshotRepositoryInMemory
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.equals.shouldBeEqual import io.kotest.matchers.equals.shouldBeEqual
@@ -31,6 +31,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -174,8 +175,8 @@ class GameStateTest :
val eventStore by inject<GameEventStore>() val eventStore by inject<GameEventStore>()
val playerNotificationListener by inject<PlayerNotificationEventListener>() val playerNotificationListener by inject<PlayerNotificationEventListener>()
ReactionEventListener(get(), get(), get()).init() ReactionEventListener(get(), get(), get()).init()
playerNotificationListener.startListening(channelNotification1, player1) playerNotificationListener.startListening({ channelNotification1.trySendBlocking(it) }, player1)
playerNotificationListener.startListening(channelNotification2, player2) playerNotificationListener.startListening({ channelNotification2.trySendBlocking(it) }, player2)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
commandHandler.handle(player1, channelCommand1, channelNotification1) commandHandler.handle(player1, channelCommand1, channelNotification1)