Implement first routes & tests
install Koin install kotest declare first events create EventStream
This commit is contained in:
14
src/main/kotlin/eventDemo/app/AggregateId.kt
Normal file
14
src/main/kotlin/eventDemo/app/AggregateId.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package eventDemo.app
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
sealed interface AggregateId {
|
||||
val id: UUID
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class GameId(override val id: UUID = UUID.randomUUID()) : AggregateId {
|
||||
constructor(id: String) : this(UUID.fromString(id))
|
||||
|
||||
override fun toString(): String = id.toString()
|
||||
}
|
||||
30
src/main/kotlin/eventDemo/app/Card.kt
Normal file
30
src/main/kotlin/eventDemo/app/Card.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package eventDemo.app
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface Card {
|
||||
@Serializable
|
||||
enum class Color {
|
||||
Blue,
|
||||
Red,
|
||||
Yellow,
|
||||
Green,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("Simple")
|
||||
data class Simple(
|
||||
val number: Int,
|
||||
val color: Color,
|
||||
) : Card
|
||||
|
||||
sealed interface Special : Card
|
||||
|
||||
@Serializable
|
||||
@SerialName("Reverse")
|
||||
data class ReverseCard(
|
||||
val color: Color,
|
||||
) : Special
|
||||
}
|
||||
27
src/main/kotlin/eventDemo/app/EventStream.kt
Normal file
27
src/main/kotlin/eventDemo/app/EventStream.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package eventDemo.app
|
||||
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
|
||||
class EventStream<ID : AggregateId> {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private val eventBus: MutableMap<ID, MutableList<Event<ID>>> = mutableMapOf()
|
||||
|
||||
fun publish(event: Event<ID>) {
|
||||
eventBus.getOrPut(event.aggregateId) { mutableListOf() }.add(event)
|
||||
logger.atInfo {
|
||||
message = "Event published"
|
||||
payload = mapOf("event" to event)
|
||||
}
|
||||
}
|
||||
|
||||
fun <U : Event<ID>> read(
|
||||
aggregateId: ID,
|
||||
eventClass: Class<U>,
|
||||
): U? {
|
||||
return eventBus.get(aggregateId)?.filterIsInstance(eventClass)?.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified U : Event<ID>, ID : AggregateId> EventStream<ID>.read(aggregateId: ID): U? {
|
||||
return this.read(aggregateId, U::class.java)
|
||||
}
|
||||
10
src/main/kotlin/eventDemo/app/Events.kt
Normal file
10
src/main/kotlin/eventDemo/app/Events.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package eventDemo.app
|
||||
|
||||
sealed interface Event<ID : AggregateId> {
|
||||
val aggregateId: ID
|
||||
}
|
||||
|
||||
data class PlayCardEvent(
|
||||
override val aggregateId: GameId,
|
||||
val card: Card,
|
||||
) : Event<GameId>
|
||||
54
src/main/kotlin/eventDemo/app/actions/PutCard.kt
Normal file
54
src/main/kotlin/eventDemo/app/actions/PutCard.kt
Normal file
@@ -0,0 +1,54 @@
|
||||
package eventDemo.app.actions
|
||||
|
||||
import eventDemo.app.Card
|
||||
import eventDemo.app.EventStream
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.PlayCardEvent
|
||||
import eventDemo.app.read
|
||||
import eventDemo.plugins.GameIdSerializer
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.resources.Resource
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.resources.get
|
||||
import io.ktor.server.resources.post
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondNullable
|
||||
import io.ktor.server.routing.Routing
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.koin.ktor.ext.inject
|
||||
|
||||
@Serializable
|
||||
@Resource("/game/{id}")
|
||||
class Game(
|
||||
@Serializable(with = GameIdSerializer::class)
|
||||
val id: GameId,
|
||||
) {
|
||||
@Serializable
|
||||
@Resource("card")
|
||||
class Card(val game: Game) {
|
||||
@Serializable
|
||||
@Resource("")
|
||||
class PutCard(val card: Card)
|
||||
|
||||
@Serializable
|
||||
@Resource("last")
|
||||
class LastCard(val card: Card)
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.putCard() {
|
||||
val eventStream by inject<EventStream<GameId>>()
|
||||
|
||||
post<Game.Card.PutCard> {
|
||||
val card = call.receive<Card.Simple>()
|
||||
eventStream.publish(PlayCardEvent(it.card.game.id, card))
|
||||
call.respondNullable<Any?>(HttpStatusCode.OK, null)
|
||||
}
|
||||
|
||||
get<Game.Card.LastCard> {
|
||||
eventStream.read<PlayCardEvent, GameId>(it.card.game.id)
|
||||
?.let { it1 -> call.respond<Card>(it1.card) }
|
||||
?: call.response.status(HttpStatusCode.BadRequest)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user