Projection was now built on listener events
Create ProjectionBus and use it with listeners add EventStream::getByVersion
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -111,5 +111,6 @@ fun GameState.apply(event: GameEvent): GameState =
|
|||||||
}
|
}
|
||||||
}.copy(
|
}.copy(
|
||||||
lastEventVersion = event.version,
|
lastEventVersion = event.version,
|
||||||
|
lastEvent = event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -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}" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user