refactoring
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package eventDemo
|
||||
|
||||
import eventDemo.configuration.configure
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package eventDemo
|
||||
|
||||
import eventDemo.app.actions.GameEventReactionSubscriber
|
||||
import eventDemo.plugins.configureHttp
|
||||
import eventDemo.plugins.configureHttpRouting
|
||||
import eventDemo.plugins.configureKoin
|
||||
import eventDemo.plugins.configureSecurity
|
||||
import eventDemo.plugins.configureSerialization
|
||||
import eventDemo.plugins.configureSockets
|
||||
import eventDemo.plugins.configureWebSocketsGameRoute
|
||||
import io.ktor.server.application.Application
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
fun Application.configure() {
|
||||
configureKoin()
|
||||
|
||||
configureSecurity()
|
||||
|
||||
configureSerialization()
|
||||
|
||||
configureSockets()
|
||||
configureWebSocketsGameRoute(get(), get())
|
||||
|
||||
configureHttp()
|
||||
configureHttpRouting()
|
||||
|
||||
GameEventReactionSubscriber(get(), get())
|
||||
.init()
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package eventDemo.app.actions
|
||||
|
||||
import eventDemo.app.actions.playNewCard.PlayCardCommand
|
||||
import eventDemo.shared.command.GameCommandStream
|
||||
import eventDemo.shared.entity.Player
|
||||
import eventDemo.shared.event.CardIsPlayedEvent
|
||||
import eventDemo.shared.event.GameEvent
|
||||
import eventDemo.shared.event.GameEventStream
|
||||
import eventDemo.shared.event.GameState
|
||||
import eventDemo.shared.event.buildStateFromEventStream
|
||||
import eventDemo.app.GameState
|
||||
import eventDemo.app.command.GameCommand
|
||||
import eventDemo.app.command.GameCommandStream
|
||||
import eventDemo.app.command.PlayCardCommand
|
||||
import eventDemo.app.entity.Player
|
||||
import eventDemo.app.event.CardIsPlayedEvent
|
||||
import eventDemo.app.event.GameEvent
|
||||
import eventDemo.app.event.GameEventStream
|
||||
import eventDemo.app.event.buildStateFromEventStream
|
||||
import io.ktor.websocket.Frame
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -17,7 +18,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Listen [PlayCardCommand] on [GameCommandStream], check the validity and execute the action.
|
||||
* Listen [GameCommand] on [GameCommandStream], check the validity and execute an action.
|
||||
*
|
||||
* This action can be executing an action and produce a new [GameEvent] after verification.
|
||||
*/
|
||||
@@ -64,10 +65,10 @@ class GameCommandHandler(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun GameState.commandCardCanBeExecuted(command: PlayCardCommand): Boolean =
|
||||
canBePlayThisCard(
|
||||
command.payload.player,
|
||||
command.payload.card,
|
||||
)
|
||||
private fun GameState.commandCardCanBeExecuted(command: PlayCardCommand): Boolean =
|
||||
canBePlayThisCard(
|
||||
command.payload.player,
|
||||
command.payload.card,
|
||||
)
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
package eventDemo.app.actions
|
||||
package eventDemo.app
|
||||
|
||||
import eventDemo.app.event.GameEvent
|
||||
import eventDemo.libs.event.EventBus
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.event.GameEvent
|
||||
import eventDemo.shared.toFrame
|
||||
import io.ktor.websocket.Frame
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class GameEventPlayerNotificationSubscriber(
|
||||
class GameEventPlayerNotificationListener(
|
||||
private val eventBus: EventBus<GameEvent, GameId>,
|
||||
private val outgoing: SendChannel<Frame>,
|
||||
) {
|
||||
@@ -1,13 +1,12 @@
|
||||
package eventDemo.app.actions
|
||||
package eventDemo.app
|
||||
|
||||
import eventDemo.app.event.GameEvent
|
||||
import eventDemo.app.event.GameStartedEvent
|
||||
import eventDemo.app.event.buildStateFromEventStream
|
||||
import eventDemo.libs.event.EventBus
|
||||
import eventDemo.libs.event.EventStream
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.event.GameEvent
|
||||
import eventDemo.shared.event.GameStartedEvent
|
||||
import eventDemo.shared.event.buildStateFromEventStream
|
||||
|
||||
class GameEventReactionSubscriber(
|
||||
class GameEventReactionListener(
|
||||
private val eventBus: EventBus<GameEvent, GameId>,
|
||||
private val eventStream: EventStream<GameEvent, GameId>,
|
||||
) {
|
||||
@@ -1,7 +1,7 @@
|
||||
package eventDemo.shared
|
||||
package eventDemo.app
|
||||
|
||||
import eventDemo.configuration.GameIdSerializer
|
||||
import eventDemo.libs.event.AggregateId
|
||||
import eventDemo.plugins.GameIdSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package eventDemo.shared.event
|
||||
package eventDemo.app
|
||||
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.entity.Card
|
||||
import eventDemo.shared.entity.Deck
|
||||
import eventDemo.shared.entity.Player
|
||||
import eventDemo.app.entity.Card
|
||||
import eventDemo.app.entity.Deck
|
||||
import eventDemo.app.entity.Player
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GameState(
|
||||
val gameId: GameId,
|
||||
val players: Set<Player> = emptySet(),
|
||||
@@ -16,6 +17,7 @@ data class GameState(
|
||||
val deck: Deck = Deck(players.toList()),
|
||||
val isStarted: Boolean = false,
|
||||
) {
|
||||
@Serializable
|
||||
data class LastCard(
|
||||
val card: Card,
|
||||
val player: Player,
|
||||
@@ -1,10 +1,11 @@
|
||||
package eventDemo.app.actions.readLastPlayedCard
|
||||
package eventDemo.app.actions
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.event.CardIsPlayedEvent
|
||||
import eventDemo.app.event.GameEventStream
|
||||
import eventDemo.app.event.buildStateFromEventStream
|
||||
import eventDemo.configuration.GameIdSerializer
|
||||
import eventDemo.libs.event.readLastOf
|
||||
import eventDemo.plugins.GameIdSerializer
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.event.CardIsPlayedEvent
|
||||
import eventDemo.shared.event.GameEventStream
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.resources.Resource
|
||||
import io.ktor.server.application.call
|
||||
@@ -25,6 +26,12 @@ class Game(
|
||||
class Card(
|
||||
val game: Game,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@Resource("state")
|
||||
class State(
|
||||
val game: Game,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,10 +43,25 @@ fun Routing.readLastPlayedCard() {
|
||||
/*
|
||||
* Read the last played card on the game.
|
||||
*/
|
||||
get<Game.Card> { card ->
|
||||
get<Game.Card> { body ->
|
||||
eventStream
|
||||
.readLastOf<CardIsPlayedEvent, _, _>(card.game.id)
|
||||
.readLastOf<CardIsPlayedEvent, _, _>(body.game.id)
|
||||
?.let { call.respond(it.card) }
|
||||
?: call.response.status(HttpStatusCode.BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API route to read the last card played.
|
||||
*/
|
||||
fun Routing.readGameState() {
|
||||
val eventStream by inject<GameEventStream>()
|
||||
|
||||
/*
|
||||
* Read the last played card on the game.
|
||||
*/
|
||||
get<Game.State> { body ->
|
||||
val state = body.game.id.buildStateFromEventStream(eventStream)
|
||||
call.respond(state)
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package eventDemo.app.actions.playNewCard
|
||||
|
||||
import eventDemo.libs.command.send
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.command.GameCommandStreamInMemory
|
||||
import eventDemo.shared.entity.Card
|
||||
import eventDemo.shared.entity.Player
|
||||
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.auth.principal
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.resources.post
|
||||
import io.ktor.server.response.respondNullable
|
||||
import io.ktor.server.routing.Routing
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Resource("/game/{id}")
|
||||
class GameRoute(
|
||||
// @Serializable(with = GameIdSerializer::class)
|
||||
val id: GameId,
|
||||
) {
|
||||
@Serializable
|
||||
@Resource("card")
|
||||
class Card(
|
||||
val game: GameRoute,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* API route to send a request to play card.
|
||||
*/
|
||||
fun Routing.playNewCard() {
|
||||
val commandStream = GameCommandStreamInMemory()
|
||||
authenticate {
|
||||
/*
|
||||
* A player request to play a new card.
|
||||
*
|
||||
* It always returns [HttpStatusCode.OK], but it is not mean that card is already played!
|
||||
*/
|
||||
post<GameRoute.Card> {
|
||||
val card = call.receive<Card>()
|
||||
val name = call.principal<Player>()!!
|
||||
launch(Dispatchers.Default) {
|
||||
commandStream.send(
|
||||
PlayCardCommand(
|
||||
it.game.id,
|
||||
name,
|
||||
card,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
call.respondNullable<Any?>(HttpStatusCode.OK, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package eventDemo.shared.command
|
||||
package eventDemo.app.command
|
||||
|
||||
import eventDemo.app.actions.playNewCard.GameCommand
|
||||
import eventDemo.libs.command.CommandStream
|
||||
import eventDemo.libs.command.CommandStreamChannel
|
||||
import eventDemo.libs.command.CommandStreamInMemory
|
||||
@@ -1,10 +1,10 @@
|
||||
package eventDemo.app.actions.playNewCard
|
||||
package eventDemo.app.command
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.entity.Card
|
||||
import eventDemo.app.entity.Player
|
||||
import eventDemo.libs.command.Command
|
||||
import eventDemo.libs.command.CommandId
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.entity.Card
|
||||
import eventDemo.shared.entity.Player
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package eventDemo.shared.entity
|
||||
package eventDemo.app.entity
|
||||
|
||||
import eventDemo.plugins.UUIDSerializer
|
||||
import eventDemo.configuration.UUIDSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
@@ -1,4 +1,4 @@
|
||||
package eventDemo.shared.entity
|
||||
package eventDemo.app.entity
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package eventDemo.shared.entity
|
||||
package eventDemo.app.entity
|
||||
|
||||
import eventDemo.configuration.PlayerIdSerializer
|
||||
import eventDemo.configuration.UUIDSerializer
|
||||
import eventDemo.libs.event.AggregateId
|
||||
import eventDemo.plugins.PlayerIdSerializer
|
||||
import eventDemo.plugins.UUIDSerializer
|
||||
import io.ktor.server.auth.Principal
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
@@ -1,10 +1,10 @@
|
||||
package eventDemo.shared.event
|
||||
package eventDemo.app.event
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.entity.Card
|
||||
import eventDemo.app.entity.Deck
|
||||
import eventDemo.app.entity.Player
|
||||
import eventDemo.libs.event.Event
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.entity.Card
|
||||
import eventDemo.shared.entity.Deck
|
||||
import eventDemo.shared.entity.Player
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,7 @@
|
||||
package eventDemo.shared.event
|
||||
package eventDemo.app.event
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.libs.event.EventBus
|
||||
import eventDemo.shared.GameId
|
||||
|
||||
class GameEventBus(
|
||||
bus: EventBus<GameEvent, GameId>,
|
||||
@@ -1,8 +1,8 @@
|
||||
package eventDemo.shared.event
|
||||
package eventDemo.app.event
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.libs.event.EventBus
|
||||
import eventDemo.libs.event.EventStream
|
||||
import eventDemo.shared.GameId
|
||||
|
||||
/**
|
||||
* A stream to publish and read the played card event.
|
||||
@@ -1,8 +1,9 @@
|
||||
package eventDemo.shared.event
|
||||
package eventDemo.app.event
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.GameState
|
||||
import eventDemo.app.entity.Card
|
||||
import eventDemo.libs.event.EventStream
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.entity.Card
|
||||
|
||||
fun GameId.buildStateFromEventStream(eventStream: EventStream<GameEvent, GameId>): GameState =
|
||||
buildStateFromEvents(
|
||||
@@ -61,7 +62,7 @@ private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
|
||||
is GameStartedEvent -> {
|
||||
state.copy(
|
||||
lastColor = (event.deck.discard.first() as? Card.ColorCard)?.color,
|
||||
lastCard = GameState.LastCard(event.deck.discard.first(), event.firstPlayer),
|
||||
lastCard = eventDemo.app.GameState.LastCard(event.deck.discard.first(), event.firstPlayer),
|
||||
lastPlayer = event.firstPlayer,
|
||||
deck = event.deck,
|
||||
isStarted = true,
|
||||
22
src/main/kotlin/eventDemo/configuration/Configure.kt
Normal file
22
src/main/kotlin/eventDemo/configuration/Configure.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.GameEventReactionListener
|
||||
import io.ktor.server.application.Application
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
fun Application.configure() {
|
||||
configureKoin()
|
||||
|
||||
configureSecurity()
|
||||
|
||||
configureSerialization()
|
||||
|
||||
configureSockets()
|
||||
configureWebSocketsGameRoute(get(), get())
|
||||
|
||||
configureHttp()
|
||||
configureHttpRouting()
|
||||
|
||||
GameEventReactionListener(get(), get())
|
||||
.init()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package eventDemo.plugins
|
||||
package eventDemo.configuration
|
||||
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpMethod
|
||||
@@ -1,9 +1,9 @@
|
||||
package eventDemo.plugins
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.event.GameEventBus
|
||||
import eventDemo.app.event.GameEventStream
|
||||
import eventDemo.libs.event.EventBusInMemory
|
||||
import eventDemo.libs.event.EventStreamInMemory
|
||||
import eventDemo.shared.event.GameEventBus
|
||||
import eventDemo.shared.event.GameEventStream
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import org.koin.dsl.module
|
||||
@@ -1,7 +1,7 @@
|
||||
package eventDemo.plugins
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.actions.playNewCard.playNewCard
|
||||
import eventDemo.app.actions.readLastPlayedCard.readLastPlayedCard
|
||||
import eventDemo.app.actions.readGameState
|
||||
import eventDemo.app.actions.readLastPlayedCard
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
@@ -24,7 +24,7 @@ fun Application.configureHttpRouting() {
|
||||
}
|
||||
|
||||
routing {
|
||||
playNewCard()
|
||||
readLastPlayedCard()
|
||||
readGameState()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package eventDemo.plugins
|
||||
package eventDemo.configuration
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
@@ -9,7 +9,6 @@ import io.ktor.server.auth.authentication
|
||||
import io.ktor.server.auth.jwt.JWTPrincipal
|
||||
import io.ktor.server.auth.jwt.jwt
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.post
|
||||
import io.ktor.server.routing.routing
|
||||
import java.util.Date
|
||||
@@ -1,8 +1,8 @@
|
||||
package eventDemo.plugins
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.entity.Player.PlayerId
|
||||
import eventDemo.libs.command.CommandId
|
||||
import eventDemo.shared.GameId
|
||||
import eventDemo.shared.entity.Player.PlayerId
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
@@ -1,10 +1,10 @@
|
||||
package eventDemo.plugins
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.GameEventPlayerNotificationListener
|
||||
import eventDemo.app.actions.GameCommandHandler
|
||||
import eventDemo.app.actions.GameEventPlayerNotificationSubscriber
|
||||
import eventDemo.shared.entity.Player
|
||||
import eventDemo.shared.event.GameEventBus
|
||||
import eventDemo.shared.event.GameEventStream
|
||||
import eventDemo.app.entity.Player
|
||||
import eventDemo.app.event.GameEventBus
|
||||
import eventDemo.app.event.GameEventStream
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import io.ktor.server.application.install
|
||||
@@ -35,7 +35,7 @@ fun Application.configureWebSocketsGameRoute(
|
||||
authenticate {
|
||||
webSocket("/game") {
|
||||
GameCommandHandler(eventStream, incoming, outgoing).init(call.getPlayer())
|
||||
GameEventPlayerNotificationSubscriber(eventBus, outgoing).init()
|
||||
GameEventPlayerNotificationListener(eventBus, outgoing).init()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package eventDemo.libs.command
|
||||
|
||||
import eventDemo.plugins.CommandIdSerializer
|
||||
import eventDemo.configuration.CommandIdSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ interface EventStream<E : Event<ID>, ID : AggregateId> {
|
||||
fun <R : E> readLastOf(
|
||||
aggregateId: ID,
|
||||
eventType: KClass<out R>,
|
||||
): E?
|
||||
): R?
|
||||
|
||||
/** Reads all events associated with a given aggregate ID */
|
||||
fun readAll(aggregateId: ID): List<E>
|
||||
|
||||
@@ -37,5 +37,5 @@ class EventStreamInMemory<E : Event<ID>, ID : AggregateId> : EventStream<E, ID>
|
||||
override fun readAll(aggregateId: ID): List<E> = events
|
||||
}
|
||||
|
||||
inline fun <reified R : E, E : Event<ID>, ID : AggregateId> EventStreamInMemory<E, ID>.readLastOf(aggregateId: ID): R? =
|
||||
inline fun <reified R : E, E : Event<ID>, ID : AggregateId> EventStream<E, ID>.readLastOf(aggregateId: ID): R? =
|
||||
readLastOf(aggregateId, R::class)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eventDemo.shared
|
||||
|
||||
import eventDemo.app.actions.playNewCard.GameCommand
|
||||
import eventDemo.shared.event.GameEvent
|
||||
import eventDemo.app.command.GameCommand
|
||||
import eventDemo.app.event.GameEvent
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
Reference in New Issue
Block a user