Move file and add interface to improve Archi Hexa
This commit is contained in:
@@ -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>()
|
||||
@@ -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()
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package eventDemo.adapter.infrastructureLayer.event.projection
|
||||
|
||||
import eventDemo.business.entity.GameId
|
||||
import eventDemo.business.event.GameEventHandler
|
||||
import eventDemo.business.event.GameEventStore
|
||||
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 GameStateRepositoryInMemory(
|
||||
eventStore: GameEventStore,
|
||||
eventHandler: GameEventHandler,
|
||||
snapshotConfig: SnapshotConfig = SnapshotConfig(),
|
||||
) : GameStateRepository {
|
||||
private val projectionsSnapshot =
|
||||
ProjectionSnapshotRepositoryInMemory(
|
||||
eventStore = eventStore,
|
||||
snapshotCacheConfig = snapshotConfig,
|
||||
applyToProjection = GameState::apply,
|
||||
initialStateBuilder = { aggregateId: GameId -> GameState(aggregateId) },
|
||||
)
|
||||
|
||||
init {
|
||||
eventHandler.registerProjectionBuilder { event ->
|
||||
projectionsSnapshot.applyAndPutToCache(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last version of the [GameState] from the all eventStream.
|
||||
*
|
||||
* It fetches it from the local cache if possible, otherwise it builds it.
|
||||
*/
|
||||
override fun getLast(gameId: GameId): GameState =
|
||||
projectionsSnapshot.getLast(gameId)
|
||||
|
||||
/**
|
||||
* Get the [GameState] to the specific [event][GameEvent].
|
||||
* It does not contain the [events][GameEvent] it after this one.
|
||||
*
|
||||
* It fetches it from the local cache if possible, otherwise it builds it.
|
||||
*/
|
||||
override fun getUntil(event: GameEvent): GameState =
|
||||
projectionsSnapshot.getUntil(event)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package eventDemo.adapter.interfaceLayer
|
||||
|
||||
import eventDemo.business.command.GameCommandHandler
|
||||
import eventDemo.business.entity.Player
|
||||
import eventDemo.business.event.eventListener.PlayerNotificationEventListener
|
||||
import eventDemo.business.notification.Notification
|
||||
import eventDemo.libs.fromFrameChannel
|
||||
import eventDemo.libs.toObjectChannel
|
||||
import io.github.oshai.kotlinlogging.withLoggingContext
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.auth.jwt.JWTPrincipal
|
||||
import io.ktor.server.auth.principal
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.websocket.webSocket
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
fun Route.gameWebSocket(
|
||||
playerNotificationListener: PlayerNotificationEventListener,
|
||||
commandHandler: GameCommandHandler,
|
||||
) {
|
||||
authenticate {
|
||||
webSocket("/game") {
|
||||
val currentPlayer = call.getPlayer()
|
||||
val outgoingFrameChannel: SendChannel<Notification> = fromFrameChannel(outgoing)
|
||||
withLoggingContext("currentPlayer" to currentPlayer.toString()) {
|
||||
GlobalScope.launch {
|
||||
commandHandler.handle(
|
||||
currentPlayer,
|
||||
toObjectChannel(incoming),
|
||||
outgoingFrameChannel,
|
||||
)
|
||||
}
|
||||
playerNotificationListener.startListening({ outgoingFrameChannel.trySendBlocking(it) }, currentPlayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ApplicationCall.getPlayer() =
|
||||
principal<JWTPrincipal>()!!.run {
|
||||
Player(
|
||||
id = payload.getClaim("playerid").asString(),
|
||||
name = payload.getClaim("username").asString(),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package eventDemo.adapter.interfaceLayer
|
||||
|
||||
import eventDemo.business.entity.GameId
|
||||
import eventDemo.business.event.projection.GameStateRepository
|
||||
import eventDemo.configuration.GameIdSerializer
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.resources.Resource
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.resources.get
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Resource("/game/{id}")
|
||||
class Game(
|
||||
@Serializable(with = GameIdSerializer::class)
|
||||
val id: GameId,
|
||||
) {
|
||||
@Serializable
|
||||
@Resource("card/last")
|
||||
class Card(
|
||||
val game: Game,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@Resource("state")
|
||||
class State(
|
||||
val game: Game,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* API routes to read the game state.
|
||||
*/
|
||||
fun Route.readTheGameState(gameStateRepository: GameStateRepository) {
|
||||
authenticate {
|
||||
// Read the last played card on the game.
|
||||
get<Game.Card> { body ->
|
||||
gameStateRepository
|
||||
.getLast(body.game.id)
|
||||
.cardOnCurrentStack
|
||||
?.card
|
||||
?.let { call.respond(it) }
|
||||
?: call.response.status(HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
// Read the last played card on the game.
|
||||
get<Game.State> { body ->
|
||||
val state = gameStateRepository.getLast(body.game.id)
|
||||
call.respond(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user