From de3d4a1021480089b9c1efb133dc85ea3014ce05 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Mon, 3 Mar 2025 21:08:21 +0100 Subject: [PATCH] Add Comments --- .../app/actions/playNewCard/HttpInput.kt | 3 +++ .../actions/playNewCard/PlayCardCommand.kt | 3 +++ .../playNewCard/PlayCardCommandHandler.kt | 6 ++++- .../actions/readLastPlayedCard/HttpInput.kt | 3 +++ .../kotlin/eventDemo/libs/command/Command.kt | 8 +++++++ .../eventDemo/libs/command/CommandStream.kt | 12 ++++++++-- .../libs/command/CommandStreamInMemory.kt | 12 +++++----- src/main/kotlin/eventDemo/libs/event/Event.kt | 8 +++++++ .../eventDemo/libs/event/EventStream.kt | 8 +++++++ .../libs/event/EventStreamInMemory.kt | 5 ++++ .../eventDemo/plugins/CommandHandler.kt | 7 +++++- src/main/kotlin/eventDemo/plugins/Koin.kt | 4 ++-- src/main/kotlin/eventDemo/shared/GameId.kt | 4 ++++ .../shared/command/GameCommandStream.kt | 3 +++ .../kotlin/eventDemo/shared/entity/Card.kt | 24 +++++++++++++++++++ .../kotlin/eventDemo/shared/entity/Game.kt | 3 +++ .../shared/event/CardIsPlayedEvent.kt | 7 ++++++ .../eventDemo/shared/event/GameEventStream.kt | 3 +++ 18 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/eventDemo/app/actions/playNewCard/HttpInput.kt b/src/main/kotlin/eventDemo/app/actions/playNewCard/HttpInput.kt index 54cf683..dadf4ad 100644 --- a/src/main/kotlin/eventDemo/app/actions/playNewCard/HttpInput.kt +++ b/src/main/kotlin/eventDemo/app/actions/playNewCard/HttpInput.kt @@ -31,6 +31,9 @@ class GameRoute( ) } +/** + * API route to send a request to play card. + */ fun Routing.playNewCard() { val commandStream by inject() diff --git a/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommand.kt b/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommand.kt index 888be79..368a5e1 100644 --- a/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommand.kt +++ b/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommand.kt @@ -7,6 +7,9 @@ import eventDemo.shared.entity.Game import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * A command to perform an action to play a new card + */ @Serializable @SerialName("PlayCard") data class PlayCardCommand( diff --git a/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommandHandler.kt b/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommandHandler.kt index 973d97c..5864ab9 100644 --- a/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommandHandler.kt +++ b/src/main/kotlin/eventDemo/app/actions/playNewCard/PlayCardCommandHandler.kt @@ -9,13 +9,17 @@ import kotlinx.coroutines.launch /** * Listen [PlayCardCommand] on [GameCommandStream], check the validity and execute the action. + * * This action produces a new [CardIsPlayedEvent] */ class PlayCardCommandHandler( private val commandStream: GameCommandStream, private val eventStream: GameEventStream, ) { - operator fun invoke() { + /** + * Init the handler + */ + fun init() { CoroutineScope(Dispatchers.IO).launch { commandStream.process { // TODO check the command can be executed diff --git a/src/main/kotlin/eventDemo/app/actions/readLastPlayedCard/HttpInput.kt b/src/main/kotlin/eventDemo/app/actions/readLastPlayedCard/HttpInput.kt index 7fa1297..57a1243 100644 --- a/src/main/kotlin/eventDemo/app/actions/readLastPlayedCard/HttpInput.kt +++ b/src/main/kotlin/eventDemo/app/actions/readLastPlayedCard/HttpInput.kt @@ -27,6 +27,9 @@ class Game( ) } +/** + * API route to read the last card played. + */ fun Routing.readLastPlayedCard() { val eventStream by inject() diff --git a/src/main/kotlin/eventDemo/libs/command/Command.kt b/src/main/kotlin/eventDemo/libs/command/Command.kt index 600255c..5f5ec91 100644 --- a/src/main/kotlin/eventDemo/libs/command/Command.kt +++ b/src/main/kotlin/eventDemo/libs/command/Command.kt @@ -4,6 +4,9 @@ import eventDemo.plugins.CommandIdSerializer import kotlinx.serialization.Serializable import java.util.UUID +/** + * An ID for the [Command] + */ @JvmInline @Serializable(with = CommandIdSerializer::class) value class CommandId( @@ -14,6 +17,11 @@ value class CommandId( override fun toString(): String = id.toString() } +/** + * Interface to represent a Command. + * + * A command is a request for an action. + */ interface Command { val id: CommandId val name: String diff --git a/src/main/kotlin/eventDemo/libs/command/CommandStream.kt b/src/main/kotlin/eventDemo/libs/command/CommandStream.kt index 13f6137..7cbb23f 100644 --- a/src/main/kotlin/eventDemo/libs/command/CommandStream.kt +++ b/src/main/kotlin/eventDemo/libs/command/CommandStream.kt @@ -2,6 +2,11 @@ package eventDemo.libs.command import kotlin.reflect.KClass +/** + * Represent a Command stream. + * + * The stream contains a list of all actions yet to be executed. + */ interface CommandStream { /** * Send a new [Command] to the queue. @@ -22,7 +27,7 @@ interface CommandStream { } /** - * A class to implement succes/faild action. + * A class to implement success/failed action. */ interface ComputeStatus { fun ack() @@ -30,7 +35,10 @@ interface CommandStream { fun nack() } - suspend fun process(block: CommandBlock) + /** + * Apply an action to all command income in the stream. + */ + suspend fun process(action: CommandBlock) } suspend inline fun CommandStream.send(vararg command: C) = send(C::class, *command) diff --git a/src/main/kotlin/eventDemo/libs/command/CommandStreamInMemory.kt b/src/main/kotlin/eventDemo/libs/command/CommandStreamInMemory.kt index 42e0216..f2f5300 100644 --- a/src/main/kotlin/eventDemo/libs/command/CommandStreamInMemory.kt +++ b/src/main/kotlin/eventDemo/libs/command/CommandStreamInMemory.kt @@ -31,18 +31,18 @@ abstract class CommandStreamInMemory : CommandStream { queue.send(command) } - override suspend fun process(block: CommandBlock) { + override suspend fun process(action: CommandBlock) { queue.consumeEach { command -> - compute(command, block) + compute(command, action) } for (command in queue) { - compute(command, block) + compute(command, action) } } private fun compute( command: C, - block: CommandBlock, + action: CommandBlock, ) { val status = object : CommandStream.ComputeStatus { var isSet: Boolean = false @@ -58,7 +58,7 @@ abstract class CommandStreamInMemory : CommandStream { } } - if (runCatching { status.block(command) }.isFailure) { + if (runCatching { status.action(command) }.isFailure) { markAsFailed(command) } else if (!status.isSet) { status.ack() @@ -75,7 +75,7 @@ abstract class CommandStreamInMemory : CommandStream { private fun markAsFailed(command: C) { failedCommand.add(command) logger.atWarn { - message = "Compute command FAILD and it put on the top of the stack : $command" + message = "Compute command FAILED and it put it ot the top of the stack : $command" payload = mapOf("command" to command) } } diff --git a/src/main/kotlin/eventDemo/libs/event/Event.kt b/src/main/kotlin/eventDemo/libs/event/Event.kt index ee5915a..749ea5e 100644 --- a/src/main/kotlin/eventDemo/libs/event/Event.kt +++ b/src/main/kotlin/eventDemo/libs/event/Event.kt @@ -2,10 +2,18 @@ package eventDemo.libs.event import java.util.UUID +/** + * Represent an ID for one aggregate, and it used in events + * @see Event + */ interface AggregateId { val id: UUID } +/** + * The basic interface for an Event + * @see EventStream + */ interface Event { val id: ID } diff --git a/src/main/kotlin/eventDemo/libs/event/EventStream.kt b/src/main/kotlin/eventDemo/libs/event/EventStream.kt index 3c8f3ac..b6e32bd 100644 --- a/src/main/kotlin/eventDemo/libs/event/EventStream.kt +++ b/src/main/kotlin/eventDemo/libs/event/EventStream.kt @@ -3,17 +3,25 @@ package eventDemo.libs.event import kotlinx.coroutines.flow.Flow import kotlin.reflect.KClass +/** + * Interface representing an event stream for publishing and reading domain events + */ interface EventStream, ID : AggregateId> { + /** Publishes a single event to the event stream */ fun publish(event: E) + /** Publishes multiple events to the event stream */ fun publish(vararg events: E) + /** Reads the last event associated with a given aggregate ID */ fun readLast(aggregateId: ID): E? + /** Reads the last event of a specific type associated with a given aggregate ID */ fun readLastOf( aggregateId: ID, eventType: KClass, ): E? + /** Reads all events associated with a given aggregate ID as a Flow (asynchronous stream) */ fun readAll(aggregateId: ID): Flow } diff --git a/src/main/kotlin/eventDemo/libs/event/EventStreamInMemory.kt b/src/main/kotlin/eventDemo/libs/event/EventStreamInMemory.kt index dcca5f7..c5ace22 100644 --- a/src/main/kotlin/eventDemo/libs/event/EventStreamInMemory.kt +++ b/src/main/kotlin/eventDemo/libs/event/EventStreamInMemory.kt @@ -5,6 +5,11 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlin.reflect.KClass +/** + * An In-Memory implementation of an event stream. + * + * All methods are implemented. + */ abstract class EventStreamInMemory, ID : AggregateId>( private val eventType: Class, ) : EventStream { diff --git a/src/main/kotlin/eventDemo/plugins/CommandHandler.kt b/src/main/kotlin/eventDemo/plugins/CommandHandler.kt index c17311e..9bc4fc9 100644 --- a/src/main/kotlin/eventDemo/plugins/CommandHandler.kt +++ b/src/main/kotlin/eventDemo/plugins/CommandHandler.kt @@ -4,6 +4,11 @@ import eventDemo.app.actions.playNewCard.PlayCardCommandHandler import io.ktor.server.application.Application import org.koin.java.KoinJavaComponent.getKoin +/** + * Configure the command handler for the PlayCard. + */ fun Application.configureCommandHandler() { - getKoin().get()() + getKoin() + .get() + .init() } diff --git a/src/main/kotlin/eventDemo/plugins/Koin.kt b/src/main/kotlin/eventDemo/plugins/Koin.kt index 7a370c1..7b9cb80 100644 --- a/src/main/kotlin/eventDemo/plugins/Koin.kt +++ b/src/main/kotlin/eventDemo/plugins/Koin.kt @@ -19,7 +19,7 @@ fun Application.configureKoin() { val appModule = module { - singleOf(::GameEventStream) - singleOf(::GameCommandStream) + singleOf(::GameEventStream) + singleOf(::GameCommandStream) singleOf(::PlayCardCommandHandler) } diff --git a/src/main/kotlin/eventDemo/shared/GameId.kt b/src/main/kotlin/eventDemo/shared/GameId.kt index 488cf8c..5f28a28 100644 --- a/src/main/kotlin/eventDemo/shared/GameId.kt +++ b/src/main/kotlin/eventDemo/shared/GameId.kt @@ -2,9 +2,13 @@ package eventDemo.shared import eventDemo.libs.event.AggregateId import eventDemo.plugins.GameIdSerializer +import eventDemo.shared.entity.Game import kotlinx.serialization.Serializable import java.util.UUID +/** + * An [AggregateId] for the [Game]. + */ @JvmInline @Serializable(with = GameIdSerializer::class) value class GameId( diff --git a/src/main/kotlin/eventDemo/shared/command/GameCommandStream.kt b/src/main/kotlin/eventDemo/shared/command/GameCommandStream.kt index 7919f83..dc713c2 100644 --- a/src/main/kotlin/eventDemo/shared/command/GameCommandStream.kt +++ b/src/main/kotlin/eventDemo/shared/command/GameCommandStream.kt @@ -3,4 +3,7 @@ package eventDemo.shared.command import eventDemo.app.actions.playNewCard.PlayCardCommand import eventDemo.libs.command.CommandStreamInMemory +/** + * A stream to publish and read the played card command. + */ class GameCommandStream : CommandStreamInMemory() diff --git a/src/main/kotlin/eventDemo/shared/entity/Card.kt b/src/main/kotlin/eventDemo/shared/entity/Card.kt index 30669eb..bdb1e6c 100644 --- a/src/main/kotlin/eventDemo/shared/entity/Card.kt +++ b/src/main/kotlin/eventDemo/shared/entity/Card.kt @@ -3,8 +3,14 @@ package eventDemo.shared.entity import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +/** + * A Play card + */ @Serializable sealed interface Card { + /** + * The color of a card + */ @Serializable enum class Color { Blue, @@ -13,6 +19,9 @@ sealed interface Card { Green, } + /** + * A play card with color and number + */ @Serializable @SerialName("Simple") data class NumericCard( @@ -22,30 +31,45 @@ sealed interface Card { sealed interface Special : Card + /** + * A revert card to revert the order of the turn. + */ @Serializable @SerialName("Reverse") data class ReverseCard( val color: Color, ) : Special + /** + * A pass card to pass the turn of the next player. + */ @Serializable @SerialName("Pass") data class PassCard( val color: Color, ) : Special + /** + * A play card to force the next player to take 2 card and pass the turn. + */ @Serializable @SerialName("Plus2") data class Plus2Card( val color: Color, ) : Special + /** + * A play card to force the next player to take 4 card and pass the turn. + */ @Serializable @SerialName("Plus4") data class Plus4Card( val nextColor: Color, ) : Special + /** + * A play card to change the color. + */ @Serializable @SerialName("ChangeColor") data class ChangeColorCard( diff --git a/src/main/kotlin/eventDemo/shared/entity/Game.kt b/src/main/kotlin/eventDemo/shared/entity/Game.kt index be90f2f..859bd8b 100644 --- a/src/main/kotlin/eventDemo/shared/entity/Game.kt +++ b/src/main/kotlin/eventDemo/shared/entity/Game.kt @@ -3,6 +3,9 @@ package eventDemo.shared.entity import eventDemo.shared.GameId import kotlinx.serialization.Serializable +/** + * Represent a Game + */ @Serializable data class Game( val id: GameId, diff --git a/src/main/kotlin/eventDemo/shared/event/CardIsPlayedEvent.kt b/src/main/kotlin/eventDemo/shared/event/CardIsPlayedEvent.kt index 3583431..d66b152 100644 --- a/src/main/kotlin/eventDemo/shared/event/CardIsPlayedEvent.kt +++ b/src/main/kotlin/eventDemo/shared/event/CardIsPlayedEvent.kt @@ -3,11 +3,18 @@ package eventDemo.shared.event import eventDemo.libs.event.Event import eventDemo.shared.GameId import eventDemo.shared.entity.Card +import eventDemo.shared.entity.Game +/** + * An [Event] of a [Game]. + */ sealed interface GameEvent : Event { override val id: GameId } +/** + * An [Event] to represent a played card. + */ data class CardIsPlayedEvent( override val id: GameId, val card: Card, diff --git a/src/main/kotlin/eventDemo/shared/event/GameEventStream.kt b/src/main/kotlin/eventDemo/shared/event/GameEventStream.kt index faa4f82..a85d58f 100644 --- a/src/main/kotlin/eventDemo/shared/event/GameEventStream.kt +++ b/src/main/kotlin/eventDemo/shared/event/GameEventStream.kt @@ -3,4 +3,7 @@ package eventDemo.shared.event import eventDemo.libs.event.EventStreamInMemory import eventDemo.shared.GameId +/** + * A stream to publish and read the played card event. + */ class GameEventStream : EventStreamInMemory(GameEvent::class.java)