chore: add CLAUDE.md
This commit is contained in:
@@ -0,0 +1,133 @@
|
|||||||
|
# CLAUDE.md — event-demo
|
||||||
|
|
||||||
|
Ce fichier donne le contexte du projet pour toute session Claude Code future sur ce dépôt.
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
`event-demo` est un projet démo personnel (Fabrice Lecomte) qui illustre plusieurs patterns
|
||||||
|
d'architecture backend :
|
||||||
|
|
||||||
|
- Event Sourcing
|
||||||
|
- Event-Driven (bus d'événements asynchrone)
|
||||||
|
- CQRS (séparation commandes / projections en lecture)
|
||||||
|
- Architecture Hexagonale (ports & adapters), un dossier par *bounded context*
|
||||||
|
|
||||||
|
Le cas d'usage servant de support est un jeu de cartes façon UNO (créer une partie, rejoindre,
|
||||||
|
jouer une carte, piocher, etc.), avec authentification des joueurs.
|
||||||
|
|
||||||
|
Dépôts distants configurés : `gitea` (auto-hébergé, git.gogn.synology.me — remote historique)
|
||||||
|
et `github` (`flecomte/event-demo`, miroir). Vérifier vers lequel pousser selon le contexte.
|
||||||
|
|
||||||
|
## Stack technique
|
||||||
|
|
||||||
|
- **Langage** : Kotlin 2.1.21, JDK 21 (toolchain Gradle)
|
||||||
|
- **Framework serveur** : Ktor 3.5.1 (Netty), DI via Koin 4.2.1
|
||||||
|
- **Sérialisation** : kotlinx.serialization (JSON)
|
||||||
|
- **Persistance** :
|
||||||
|
- PostgreSQL (event store, via HikariCP) + migrations Flyway (`migrations/events/`)
|
||||||
|
- RabbitMQ (bus d'événements / bus de commandes, via amqp-client)
|
||||||
|
- **Auth** : JWT (ktor-server-auth-jwt), hash de mot de passe via password4j
|
||||||
|
- **Infra dev/prod** : Docker Compose (fichiers `docker/docker-compose-{dev,test,prod}.yaml`
|
||||||
|
incluant des « parts » réutilisables dans `docker/parts/`), reverse proxy Træfik
|
||||||
|
- **Tests** : Kotest (runner JUnit5), MockK, kotest-extensions-koin, ArchUnit (test d'architecture)
|
||||||
|
- **Qualité** : ktlint (`ktlint_official`, standard + experimental activés), reporting checkstyle
|
||||||
|
- **CI** : GitHub Actions (`.github/workflows/tests.yml`) — build/cache Gradle, `ktlintCheck`,
|
||||||
|
puis tests exécutés **dans Docker** (`docker compose -f docker/docker-compose-test.yaml run tests`)
|
||||||
|
- **API** : documentée en OpenAPI (`resources/openapi/documentation.yaml`)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Un dossier par *bounded context* sous `src/main/kotlin/eventDemo/contexts/<context>/`, chacun
|
||||||
|
strictement découpé en 3 couches :
|
||||||
|
|
||||||
|
- `domain/` — aucune dépendance vers les autres couches
|
||||||
|
- `application/` — ne dépend que de `domain`
|
||||||
|
- `infrastructure/` — dépend de `domain` et `application`
|
||||||
|
|
||||||
|
Contexts actuels :
|
||||||
|
|
||||||
|
- **`auth`** : `User`, création de compte, login JWT, event store dédié (Postgresql),
|
||||||
|
projection utilisateur.
|
||||||
|
- **`game`** : cœur du jeu — `Card`, `DrawPile`/`DiscardPile`, `Player`, `GameId`, commandes
|
||||||
|
(`JoinTheGameCommand`, `PlayCardCommand`, `ReadyToPlayCommand`, `TakeCartFromDrawPileCommand`),
|
||||||
|
state machine du jeu via `sealed interface Game` (`GameInit` → `GameCreated` → `GameStarted` →
|
||||||
|
`GameEnded`), notifications, projections (liste de parties), listeners/réactions.
|
||||||
|
|
||||||
|
Libs transverses dans `libs/` (indépendantes de tout contexte) :
|
||||||
|
|
||||||
|
- `bus/` — abstraction `Bus<E>` avec implémentations in-memory et RabbitMQ (fanout exchange)
|
||||||
|
- `command/` — `Command`, `CommandUnicityChecker` (empêche la double exécution d'une commande,
|
||||||
|
cache glissant de 10 min par défaut)
|
||||||
|
- `eventSource/` — `Event`, `EventStream` (append/lecture par version, gestion de
|
||||||
|
`VersionConflictException`), `EventStore` in-memory / Postgresql
|
||||||
|
- `helpers/`, `serializer/` — utilitaires (conversion de frames WebSocket, sérialiseurs UUID, etc.)
|
||||||
|
|
||||||
|
## Patterns notables dans le code
|
||||||
|
|
||||||
|
- **Event sourcing** : `Game.loadFromHistory(events)` reconstruit l'état en repliant
|
||||||
|
(`fold`) les événements sur une state machine scellée, en utilisant la syntaxe Kotlin 2.1
|
||||||
|
`when` avec garde `if` (ex. `is GameCreatedEvent if this is GameInit -> applyEvent(event)`).
|
||||||
|
- **CQRS** : écriture via les command handlers (`application/command/handlers`), lecture via des
|
||||||
|
projections dédiées (`application/projections`), propagées via le bus RabbitMQ, pas de couplage
|
||||||
|
direct avec l'écriture.
|
||||||
|
- **Event-driven** : réactions asynchrones (`ReactionListener`, `EventToNotificationSubscriber`)
|
||||||
|
déclenchées par le bus RabbitMQ (exchange fanout, une queue par abonné).
|
||||||
|
- **Exceptions métier** : hiérarchie `GameException` / `IllegalActionException` dans
|
||||||
|
`domain/game/errors`, une exception par règle métier violée (ex.
|
||||||
|
`NeedMorePlayersToStartGameException`, `ItsNotTheTurnException`).
|
||||||
|
|
||||||
|
## Commandes utiles
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./gradlew build # build complet
|
||||||
|
./gradlew test # tests (JUnit5 via Kotest)
|
||||||
|
./gradlew ktlintCheck # lint
|
||||||
|
./gradlew ktlintFormat # auto-format
|
||||||
|
./gradlew buildFatJar # jar exécutable "all-in-one" (utilisé par le Dockerfile prod)
|
||||||
|
|
||||||
|
# Dépendances seules (Postgres, RabbitMQ, Træfik, pgAdmin...) pour lancer l'app en local hors docker
|
||||||
|
docker compose -f docker/docker-compose-dev.yaml up -d
|
||||||
|
|
||||||
|
# Stack de test façon CI
|
||||||
|
docker compose -f docker/docker-compose-test.yaml up -d
|
||||||
|
# ou directement (comme en CI) :
|
||||||
|
docker compose -f docker/docker-compose-test.yaml run tests
|
||||||
|
|
||||||
|
# Stack complète en prod
|
||||||
|
docker compose -f docker/docker-compose-prod.yaml -p event-demo up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
URLs en dev (voir `doc/installation.md`, nécessite Træfik + résolution des `*.traefik.me`) :
|
||||||
|
API sur `http://api.traefik.me/`, dashboard
|
||||||
|
Træfik, pgAdmin et RabbitMQ management exposés via des sous-domaines `traefik.me`.
|
||||||
|
|
||||||
|
## Conventions de code
|
||||||
|
|
||||||
|
- ktlint en mode `ktlint_official` + règles `standard` et `experimental` activées
|
||||||
|
(voir `.editorconfig`), indentation **2 espaces**, virgules finales (*trailing commas*)
|
||||||
|
systématiques, wrapping forcé des expressions/signatures multi-lignes.
|
||||||
|
- Fins de ligne forcées en **LF** (`.gitattributes`), sauf `gradlew.bat` en CRLF.
|
||||||
|
- Code et identifiants en anglais.
|
||||||
|
- Style Kotlin idiomatique/fonctionnel : `fold`, `let`, `apply`, `when` exhaustifs, classes/interfaces
|
||||||
|
scellées (`sealed class`/`sealed interface`) pour modéliser états et événements plutôt que des enums
|
||||||
|
avec des champs optionnels.
|
||||||
|
|
||||||
|
## Pièges connus / choses à savoir avant de toucher au build ou à la CI
|
||||||
|
|
||||||
|
- **MockK/ByteBuddy en Docker** : l'auto-attach dynamique de MockK échoue dans les conteneurs
|
||||||
|
(le handshake SIGQUIT de l'AttachListener JVM time-out). Le `build.gradle.kts` charge donc
|
||||||
|
l'agent `byte-buddy-agent` de façon statique via `-javaagent` pour les tâches `Test`, afin
|
||||||
|
que MockK détecte l'instrumentation déjà présente et saute l'attach dynamique. Ne pas retirer
|
||||||
|
ce bloc sans repenser l'exécution des tests en Docker.
|
||||||
|
- **Secret Postgres en CI** : `docker/postgresql.secret` est généré à la volée par le workflow
|
||||||
|
GitHub Actions s'il n'existe pas (`echo -n "changeit" > docker/postgresql.secret`) — normal,
|
||||||
|
pas un fichier à committer.
|
||||||
|
- Les tests « officiels » de la CI tournent **dans Docker**, pas directement via `./gradlew test`
|
||||||
|
sur l'hôte — en cas de comportement différent entre local et CI, vérifier d'abord les
|
||||||
|
variables d'environnement/versions du `docker-compose-test.yaml`.
|
||||||
|
|
||||||
|
## Historique récent (pour contexte)
|
||||||
|
|
||||||
|
Le projet a connu un « Massive refactor to build the V2 » (commit `e2d7942`) : passage d'une
|
||||||
|
architecture par couches techniques plates (`adapter/presenter/domain`) à l'organisation actuelle
|
||||||
|
par bounded context (`auth`/`game`) avec 3 couches hexagonales chacune.
|
||||||
+1
-1
@@ -21,7 +21,7 @@ docker compose -f docker\docker-compose-test.yaml up -d
|
|||||||
|
|
||||||
Api url:
|
Api url:
|
||||||
- [Backend API](http://api.traefik.me/)
|
- [Backend API](http://api.traefik.me/)
|
||||||
- [Frontend web site](http://app.traefik.me/) (WIP)
|
- [Frontend web site](http://app.traefik.me/)
|
||||||
|
|
||||||
Exposed url on dev env:
|
Exposed url on dev env:
|
||||||
- [PostgreSql](http://localhost:5432/)
|
- [PostgreSql](http://localhost:5432/)
|
||||||
|
|||||||
Reference in New Issue
Block a user