Projection was now built on listener events
Create ProjectionBus and use it with listeners add EventStream::getByVersion
This commit is contained in:
@@ -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>>
|
||||
@@ -5,6 +5,9 @@ import eventDemo.business.entity.Player
|
||||
import eventDemo.libs.event.projection.Projection
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* This [projection][Projection] is used to list all current games
|
||||
*/
|
||||
@Serializable
|
||||
data class GameList(
|
||||
override val aggregateId: GameId,
|
||||
|
||||
@@ -4,9 +4,13 @@ import eventDemo.business.entity.Card
|
||||
import eventDemo.business.entity.Deck
|
||||
import eventDemo.business.entity.GameId
|
||||
import eventDemo.business.entity.Player
|
||||
import eventDemo.business.event.event.GameEvent
|
||||
import eventDemo.libs.event.projection.Projection
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* This [projection][Projection] is used for manage a game and theirs [card][Card]
|
||||
*/
|
||||
@Serializable
|
||||
data class GameState(
|
||||
override val aggregateId: GameId,
|
||||
@@ -20,6 +24,7 @@ data class GameState(
|
||||
val deck: Deck = Deck(players),
|
||||
val isStarted: Boolean = false,
|
||||
val playerWins: Set<Player> = emptySet(),
|
||||
val lastEvent: GameEvent? = null,
|
||||
) : Projection<GameId> {
|
||||
enum class Direction {
|
||||
CLOCKWISE,
|
||||
|
||||
@@ -111,5 +111,6 @@ fun GameState.apply(event: GameEvent): GameState =
|
||||
}
|
||||
}.copy(
|
||||
lastEventVersion = event.version,
|
||||
lastEvent = event,
|
||||
)
|
||||
}
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package eventDemo.business.event.projection.projectionListener
|
||||
|
||||
import eventDemo.business.entity.Card
|
||||
import eventDemo.business.entity.GameId
|
||||
import eventDemo.business.entity.Player
|
||||
import eventDemo.business.event.event.CardIsPlayedEvent
|
||||
import eventDemo.business.event.event.GameStartedEvent
|
||||
import eventDemo.business.event.event.NewPlayerEvent
|
||||
import eventDemo.business.event.event.PlayerChoseColorEvent
|
||||
import eventDemo.business.event.event.PlayerHavePassEvent
|
||||
import eventDemo.business.event.event.PlayerReadyEvent
|
||||
import eventDemo.business.event.event.PlayerWinEvent
|
||||
import eventDemo.business.event.projection.GameProjectionBus
|
||||
import eventDemo.business.event.projection.gameState.GameState
|
||||
import eventDemo.business.notification.ItsTheTurnOfNotification
|
||||
import eventDemo.business.notification.Notification
|
||||
import eventDemo.business.notification.PlayerAsJoinTheGameNotification
|
||||
import eventDemo.business.notification.PlayerAsPlayACardNotification
|
||||
import eventDemo.business.notification.PlayerHavePassNotification
|
||||
import eventDemo.business.notification.PlayerWasChoseTheCardColorNotification
|
||||
import eventDemo.business.notification.PlayerWasReadyNotification
|
||||
import eventDemo.business.notification.PlayerWinNotification
|
||||
import eventDemo.business.notification.TheGameWasStartedNotification
|
||||
import eventDemo.business.notification.WelcomeToTheGameNotification
|
||||
import eventDemo.business.notification.YourNewCardNotification
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.github.oshai.kotlinlogging.withLoggingContext
|
||||
|
||||
class PlayerNotificationListener(
|
||||
private val projectionBus: GameProjectionBus,
|
||||
) {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
fun startListening(
|
||||
outgoingNotification: (Notification) -> Unit,
|
||||
currentPlayer: Player,
|
||||
gameId: GameId,
|
||||
) {
|
||||
projectionBus.subscribe { currentState ->
|
||||
if (currentState !is GameState) return@subscribe
|
||||
if (currentState.aggregateId != gameId) return@subscribe
|
||||
withLoggingContext("projection" to currentState.toString()) {
|
||||
fun Notification.send() {
|
||||
withLoggingContext("notification" to this.toString()) {
|
||||
if (currentState.players.contains(currentPlayer)) {
|
||||
// Only notify players who have already joined the game.
|
||||
outgoingNotification(this)
|
||||
logger.info { "Notification was SEND" }
|
||||
} else {
|
||||
// Rare use case, when a connexion is created with the channel,
|
||||
// but the player was not already join in the game
|
||||
logger.warn { "Notification was SKIP, no player on the game" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendNextTurnNotif() =
|
||||
ItsTheTurnOfNotification(
|
||||
player = currentState.currentPlayerTurn ?: error("No player turn defined"),
|
||||
).send()
|
||||
|
||||
val event =
|
||||
currentState.lastEvent
|
||||
?: error("No last event in the GameState projection")
|
||||
|
||||
when (event) {
|
||||
is NewPlayerEvent -> {
|
||||
if (currentPlayer != event.player) {
|
||||
PlayerAsJoinTheGameNotification(
|
||||
player = event.player,
|
||||
).send()
|
||||
} else {
|
||||
WelcomeToTheGameNotification(
|
||||
players = currentState.players,
|
||||
).send()
|
||||
}
|
||||
}
|
||||
|
||||
is CardIsPlayedEvent -> {
|
||||
if (currentPlayer != event.player) {
|
||||
PlayerAsPlayACardNotification(
|
||||
player = event.player,
|
||||
card = event.card,
|
||||
).send()
|
||||
}
|
||||
|
||||
if (event.card !is Card.AllColorCard) {
|
||||
ItsTheTurnOfNotification(
|
||||
player = currentState.currentPlayerTurn ?: error("No player turn defined"),
|
||||
).send()
|
||||
}
|
||||
}
|
||||
|
||||
is GameStartedEvent -> {
|
||||
TheGameWasStartedNotification(
|
||||
hand =
|
||||
event.deck.playersHands.getHand(currentPlayer)
|
||||
?: error("You are not in the game"),
|
||||
).send()
|
||||
|
||||
sendNextTurnNotif()
|
||||
}
|
||||
|
||||
is PlayerChoseColorEvent -> {
|
||||
if (currentPlayer != event.player) {
|
||||
PlayerWasChoseTheCardColorNotification(
|
||||
player = event.player,
|
||||
color = event.color,
|
||||
).send()
|
||||
}
|
||||
|
||||
sendNextTurnNotif()
|
||||
}
|
||||
|
||||
is PlayerHavePassEvent -> {
|
||||
if (currentPlayer == event.player) {
|
||||
YourNewCardNotification(
|
||||
card = event.takenCard,
|
||||
).send()
|
||||
} else {
|
||||
PlayerHavePassNotification(
|
||||
player = event.player,
|
||||
).send()
|
||||
}
|
||||
|
||||
sendNextTurnNotif()
|
||||
}
|
||||
|
||||
is PlayerReadyEvent -> {
|
||||
if (currentPlayer != event.player) {
|
||||
PlayerWasReadyNotification(
|
||||
player = event.player,
|
||||
).send()
|
||||
}
|
||||
}
|
||||
|
||||
is PlayerWinEvent -> {
|
||||
PlayerWinNotification(
|
||||
player = event.player,
|
||||
).send()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package eventDemo.business.event.projection.projectionListener
|
||||
|
||||
import eventDemo.business.entity.GameId
|
||||
import eventDemo.business.event.GameEventHandler
|
||||
import eventDemo.business.event.event.GameStartedEvent
|
||||
import eventDemo.business.event.event.PlayerWinEvent
|
||||
import eventDemo.business.event.projection.GameProjectionBus
|
||||
import eventDemo.business.event.projection.gameState.GameState
|
||||
import eventDemo.libs.event.projection.Projection
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.github.oshai.kotlinlogging.withLoggingContext
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
|
||||
class ReactionListener(
|
||||
private val projectionBus: GameProjectionBus,
|
||||
private val eventHandler: GameEventHandler,
|
||||
private val priority: Int = DEFAULT_PRIORITY,
|
||||
) {
|
||||
companion object Config {
|
||||
const val DEFAULT_PRIORITY = -1000
|
||||
val registeredListeners = ConcurrentSkipListSet<GameProjectionBus>()
|
||||
}
|
||||
|
||||
private val logger = KotlinLogging.logger { }
|
||||
|
||||
fun init() {
|
||||
if (registeredListeners.add(projectionBus)) {
|
||||
projectionBus.subscribe(priority) { projection: Projection<GameId> ->
|
||||
if (projection !is GameState) return@subscribe
|
||||
withLoggingContext("projection" to projection.toString()) {
|
||||
sendStartGameEvent(projection)
|
||||
sendWinnerEvent(projection)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error { "${this::class.java.simpleName} is already init for this bus" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendStartGameEvent(state: GameState) {
|
||||
if (state.isReady && !state.isStarted) {
|
||||
val reactionEvent =
|
||||
eventHandler.handle(state.aggregateId) {
|
||||
GameStartedEvent.new(
|
||||
id = state.aggregateId,
|
||||
players = state.players,
|
||||
version = it,
|
||||
)
|
||||
}
|
||||
logger.atInfo {
|
||||
message = "Reaction event was Send"
|
||||
payload = mapOf("reactionEvent" to reactionEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendWinnerEvent(state: GameState) {
|
||||
val winner = state.playerHasNoCardLeft().firstOrNull()
|
||||
if (winner != null) {
|
||||
val reactionEvent =
|
||||
eventHandler.handle(state.aggregateId) {
|
||||
PlayerWinEvent(
|
||||
aggregateId = state.aggregateId,
|
||||
player = winner,
|
||||
version = it,
|
||||
)
|
||||
}
|
||||
|
||||
logger.atInfo {
|
||||
message = "Reaction event was Send"
|
||||
payload = mapOf("reactionEvent" to reactionEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user