feat(events): migrate game-creation and bot flows to Redis Streams NCS-89 (#62)
Build & Test (NowChessSystems) TeamCity build finished

Replace synchronous account→core game-creation HTTP call and plain
pub/sub bot game-start events with Redis Streams using consumer groups,
XACK, retry, and a Dead Letter Queue for at-least-once delivery and
observability.

- account: GameCreationStreamClient publishes game-creation requests and
  correlates responses via a per-instance consumer group (NCS-91)
- core: GameCreationStreamListener consumes requests, calls
  GameCreationService, publishes response events, retries, and routes
  exhausted/unparseable events to the DLQ (NCS-91, NCS-93, NCS-94)
- official-bots: bot game-start events migrated from pub/sub to Streams
  with consumer group, XACK, retry, and DLQ (NCS-92)
- account EventPublisher dual-writes to the stream and legacy pub/sub
  channel for backward compatibility
- all flows use the typed EventEnvelope (eventId/type/payload/timestamp/
  correlationId) with DLQ error context (eventType, error, attempt)
- register new DTOs and EventEnvelope/EventType for native reflection

Closes NCS-91, NCS-92, NCS-93, NCS-94

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Janis Eccarius <eccariusjanis@gmail.com>
Reviewed-on: #62
This commit was merged in pull request #62.
This commit is contained in:
2026-06-09 10:31:32 +02:00
parent 11826356c1
commit a24924c230
16 changed files with 583 additions and 35 deletions
@@ -34,3 +34,5 @@ nowchess:
secret: test-secret
auth:
enabled: false
game-creation-stream:
enabled: false
@@ -1,11 +1,11 @@
package de.nowchess.account.resource
import de.nowchess.account.client.{CoreGameClient, CoreGameResponse}
import de.nowchess.account.client.GameCreationStreamClient
import de.nowchess.api.dto.GameCreationResponseDto
import io.quarkus.test.InjectMock
import io.quarkus.test.junit.QuarkusTest
import io.restassured.RestAssured
import io.restassured.http.ContentType
import org.eclipse.microprofile.rest.client.inject.RestClient
import org.hamcrest.Matchers.*
import org.junit.jupiter.api.{BeforeEach, Test}
import org.mockito.{ArgumentMatchers, Mockito}
@@ -14,14 +14,15 @@ import org.mockito.{ArgumentMatchers, Mockito}
class ChallengeResourceTest:
@InjectMock
@RestClient
// scalafix:off DisableSyntax.var
var coreGameClient: CoreGameClient = scala.compiletime.uninitialized
var gameCreationClient: GameCreationStreamClient = scala.compiletime.uninitialized
// scalafix:on
@BeforeEach
def setup(): Unit =
Mockito.when(coreGameClient.createGame(ArgumentMatchers.any())).thenReturn(CoreGameResponse("test-game-id"))
Mockito
.when(gameCreationClient.createGame(ArgumentMatchers.any()))
.thenReturn(GameCreationResponseDto(Some("test-game-id")))
private def givenRequest() = RestAssured.`given`().contentType(ContentType.JSON)