chore: initial commit

This commit is contained in:
devcodetools committed 2026-09-21 17:28:26 +02:00
commit 03f03f7ad2
39 files changed
+7292

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
@startuml c4-container
' C4 modèle — Niveau 2 : Conteneurs (syntaxe native PlantUML, compatible anciennes versions)
' Application web Flashcards (SPA/PWA)
hide stereotype
skinparam backgroundColor #FFFFFF
' ===== Styles par type =====
skinparam rectangle<<person>> {
BackgroundColor #08427B
BorderColor #073B6F
FontColor #FFFFFF
}
skinparam rectangle<<system>> {
'BackgroundColor #1168BD
BorderColor #3C7FC0
FontColor #FFFFFF
}
skinparam rectangle<<container>> {
BackgroundColor #438DD5
BorderColor #135F97
FontColor #FFFFFF
}
skinparam rectangle<<external_system>> {
BackgroundColor #999999
BorderColor #8A8A8A
FontColor #FFFFFF
}
skinparam arrow {
Color #707070
}
title Conteneurs — Flashcards (Niveau 2)
' ===== Personne =====
rectangle "Utilisateur\nNavigateur / mobile\n(accès au jeu et au CRUD)" <<person>> as User
' ===== Système Flashcards et ses conteneurs =====
rectangle "Application Flashcards [Système]\nSPA/PWA Next.js — double persistance simultanée" <<system>> as Flashcards {
rectangle "SPA React (Next.js)\n[Conteneur : Web Browser]\nRendu de l'UI, état local, navigation" <<container>> as Spa
rectangle "API Next.js / App Router\n[Conteneur : Node.js]\nCardController > CardService :\nliste, CRUD, tirage aléatoire, thèmes\n(seul conteneur qui accède aux données)" <<container>> as Api
rectangle "CardRepository (générique)\n[Abstraction de données]\nContracts : findAll, findById,\nfindRandom, findRandomByTheme,\nfindThemes, create, update, delete" <<container>> as Repo
rectangle "Implémentation MySQL\n[Conteneur : librairie]\nImplémentation SQL du repository" <<container>> as RepoMysql
rectangle "Implémentation MongoDB\n[Conteneur : librairie]\nImplémentation Mongo du repository" <<container>> as RepoMongo
}
' ===== Systèmes externes =====
rectangle "Base MySQL\n[Conteneur externe : SGBD]\nPersistance des cartes (table cards)\n+ stockage Docker avec volume" <<external_system>> as MySql
rectangle "Base MongoDB\n[Conteneur externe : SGBD]\nPersistance des cartes (collection cards)\n+ stockage Docker avec volume" <<external_system>> as Mongo
'rectangle "Docker / docker-compose\n[infrastructure]\nOrchestration des conteneurs\nweb + bases + volumes" <<external_system>> as Docker
' ===== Relations =====
User -[#707070]-> Spa : "Navigue et interagit (HTTP/HTTPS)"
Spa -[#707070]-> Api : "Appels API JSON (dialogues, CRUD,\ntirage, révélation de réponse)"
Api -[#707070]down-> Repo : "Utilise uniquement l'abstraction"
Repo -[#707070]-> RepoMysql : "implémente"
Repo -[#707070]-> RepoMongo : "implémente"
Api -[#707070]down-> MySql : "Tables SQL via repository\n(selectionné par config/driver)"
Api -[#707070]down-> Mongo : "Requêtes Mongo via repository\n(selectionné par config/driver)"
'Docker -[#707070]-> MySql : "Démarre / arrête (docker-compose)"
'Docker -[#707070]-> Mongo : "Démarre / arrête (docker-compose)"
@enduml
+55
View File
@@ -0,0 +1,55 @@
@startuml c4-context
' C4 modèle — Niveau 1 : Contexte système (syntaxe native PlantUML, compatible anciennes versions)
' Application web Flashcards (SPA/PWA)
hide stereotype
skinparam backgroundColor #FFFFFF
' ===== Styles par type =====
skinparam rectangle<<person>> {
BackgroundColor #08427B
BorderColor #073B6F
FontColor #FFFFFF
}
skinparam rectangle<<external_person>> {
BackgroundColor #686868
BorderColor #8A8A8A
FontColor #FFFFFF
}
skinparam rectangle<<system>> {
BackgroundColor #1168BD
BorderColor #3C7FC0
FontColor #FFFFFF
}
skinparam rectangle<<external_system>> {
BackgroundColor #999999
BorderColor #8A8A8A
FontColor #FFFFFF
}
skinparam arrow {
Color #707070
}
title Contexte système — Flashcards (Niveau 1)
' ===== Personnes =====
rectangle "Utilisateur\nJoueur et contributeur de cartes\n(CRUD) : code, dev, culture tech" <<person>> as User
' ===== Système à modéliser =====
rectangle "App Flashcards\nSPA/PWA Next.js — tirage de cartes au\nhasard, tirage par thème, CRUD des cartes.\nDouble persistance simultanée\nMySQL + MongoDB via couche générique." <<system>> as Flashcards
' ===== Systèmes externes =====
rectangle "MySQL\nBase relationnelle\n(conteneur Docker)" <<external_system>> as MySql
rectangle "MongoDB\nBase documentaire\n(conteneur Docker)" <<external_system>> as Mongo
'rectangle "Docker / docker-compose\nOrchestration des conteneurs\n(web, bases) + volumes persistants" <<external_system>> as Docker
User -[#707070]-> Flashcards : Joue, crée, modifie, supprime,\nconsulte et révèle des cartes
Flashcards -[#707070]-> MySql : Lit / écrit (CardRepository)
Flashcards -[#707070]-> Mongo : Lit / écrit (CardRepository)
'Docker -[#707070]-> MySql : Démarre et gère
'Docker -[#707070]-> Mongo : Démarre et gère
@enduml
+105
View File
@@ -0,0 +1,105 @@
@startuml
' Diagramme de classes — Application web Flashcards (Architecture : couche d'accès générique)
skinparam classAttributeIconSize 0
skinparam classFontStyle bold
' ===== Domaine =====
class Card {
- id : string
- question : string
- response : string
- theme : string
- tags : string[]
- createdAt : Date
- updatedAt : Date
+ Card(id, question, response, theme, tags)
+ validate() : void
}
' ===== Abstraction générique d'accès aux données =====
interface CardRepository {
+ findAll() : Promise<Card[]>
+ findById(id : string) : Promise<Card | null>
+ findRandom() : Promise<Card | null>
+ findRandomByTheme(theme : string) : Promise<Card | null>
+ findThemes() : Promise<string[]>
+ create(card : Card) : Promise<Card>
+ update(card : Card) : Promise<Card>
+ delete(id : string) : Promise<boolean>
}
' ===== Implémentations SGBD =====
class MysqlCardRepository {
- connection : MysqlConnection
+ MysqlCardRepository(connection)
}
class MongoCardRepository {
- collection : MongoCollection
+ MongoCardRepository(collection)
}
class "MysqlConnection" as ConnMysql {
- host : string
- port : number
- user : string
- password : string
- database : string
+ query(sql : string, params : unknown[]) : Promise<unknown>
}
class "MongoCollection" as ColMongo {
+ find(filter : object) : Promise<unknown[]>
+ insertOne(doc : object) : Promise<unknown>
+ updateOne(filter : object, update : object) : Promise<unknown>
+ deleteOne(filter : object) : Promise<unknown>
}
class DatabaseConfig {
- driver : string
+ getActiveDriver() : string
}
' ===== Service métier (dépend uniquement de l'abstraction) =====
class CardService {
- repository : CardRepository
+ CardService(repository : CardRepository)
+ listCards() : Promise<Card[]>
+ getCard(id : string) : Promise<Card>
+ createCard(payload : NewCardPayload) : Promise<Card>
+ updateCard(id : string, payload : UpdateCardPayload) : Promise<Card>
+ deleteCard(id : string) : Promise<void>
+ playRandom(theme? : string) : Promise<Card>
+ listThemes() : Promise<string[]>
}
' ===== Couche exposition (routes / contrôleur) =====
class CardController {
+ handleList(req, res) : Promise<void>
+ handleGetById(req, res) : Promise<void>
+ handleCreate(req, res) : Promise<void>
+ handleUpdate(req, res) : Promise<void>
+ handleDelete(req, res) : Promise<void>
+ handlePlayRandom(req, res) : Promise<void>
+ handleThemes(req, res) : Promise<void>
}
class Factory {
{static} buildCardRepository(config : DatabaseConfig) : CardRepository
}
' ===== Relations =====
CardRepository <|.. MysqlCardRepository
CardRepository <|.. MongoCardRepository
MysqlCardRepository *-- ConnMysql : utilise >
MongoCardRepository *-- ColMongo : utilise >
CardService *-- CardRepository : <<injecté>> utilise >
CardService ..> Card : < crée/retourne
CardController --> CardService : utilise
Factory ..> CardRepository : <<instance de>> construit
Factory ..> CardService : construit
@enduml
+74
View File
@@ -0,0 +1,74 @@
@startuml
' Diagramme de séquence — "Afficher une carte" puis "Révéler la réponse"
' Application web Flashcards (SPA/PWA, couche d'accès générique MySQL/Mongo)
hide footbox
title Jeu : afficher une carte au hasard puis révéler la réponse
actor Utilisateur as User
participant "SPA React\n(UI)" as SPA
participant "CardController\n(API Next.js)" as Api
participant "CardService" as Service
participant "CardRepository\n(abstraction générique)" as Repo
participant "SGBD\n(MySQL OU MongoDB)" as DB
== Afficher une carte au hasard ==
User -> SPA : clique « Jouer »
SPA -> SPA : un thème est-il déjà sélectionné ?
alt Thème précis sélectionné (F-04)
User -> SPA : sélectionne un thème (ex. javascript)
SPA -> Api : GET /api/cards/random?theme=javascript
else Tirage global (F-01)
SPA -> Api : GET /api/cards/random
end
Api -> Service : playRandom(theme?)
activate Service
alt theme fourni
Service -> Repo : findRandomByTheme(theme)
else sans thème
Service -> Repo : findRandom()
end
activate Repo
Repo -> DB : Requête aléatoire (ORDER BY RAND() / $sample)
activate DB
DB --> Repo : Enregistrement(s) : question, réponse, thème
deactivate DB
Repo --> Service : Card
deactivate Repo
Service --> Api : Card (réponse non renvoyée par sécurité)
deactivate Service
Api --> SPA : 200 JSON { id, question, theme }
SPA -> SPA : affiche la question\nla réponse reste masquée
== Révéler la réponse ==
User -> SPA : clique « Voir la réponse » (F-03)
SPA -> Api : GET /api/cards/{id}/reveal
activate Api
Api -> Repo : findById(id)
activate Repo
Repo -> DB : SELECT par id / findOne
activate DB
DB --> Repo : Carte complète
deactivate DB
Repo --> Api : Card { response }
deactivate Repo
Api --> SPA : 200 JSON { response }
deactivate Api
SPA --> SPA : déroule et affiche la réponse
User -> SPA : reclique (masquer/réafficher) — boucle libre
loop tant que joueur le souhaite
SPA --> User : réponse visible / masquée
end
note over Repo : L'implémentation concrète (MySQL ou MongoDB)\nest fournie par la Factory selon DatabaseConfig\nsans impact sur Service ni Api (générique).
@enduml
+45
View File
@@ -0,0 +1,45 @@
@startuml Usecase
' Diagramme de cas d'utilisation — Application web Flashcards (SPA/PWA)
' Le CRUD et le jeu sont volontairement isolés : le jeu ne dépend d'aucun cas du catalogue.
left to right direction
skinparam actorStyle awesome
skinparam packageStyle rectangle
actor "Utilisateur" as User
rectangle "Jeu — révision" {
usecase "Afficher une carte au hasard" as UC_Jouer
usecase "Tirer des cartes d'un thème" as UC_Theme
usecase "Révéler la réponse" as UC_Revele
}
rectangle "Gestion des cartes (CRUD)" {
usecase "Lister les cartes" as UC_Liste
usecase "Consulter une carte" as UC_Detail
usecase "Créer une carte" as UC_Create
usecase "Modifier une carte" as UC_Update
usecase "Supprimer une carte" as UC_Delete
}
' --- Jeu ---
User --> UC_Jouer
UC_Jouer ..> UC_Theme : <<extend>>
UC_Jouer --> UC_Revele : <<include>>
' --- CRUD : la liste est l'entrée unique, le détail est le point d'ancrage des actions ---
User --> UC_Liste
UC_Liste ..> UC_Detail : <<extend>>
UC_Liste ..> UC_Create : <<extend>>
UC_Detail ..> UC_Update : <<extend>>
UC_Detail ..> UC_Delete : <<extend>>
note bottom of UC_Delete
Suppression avec confirmation
end note
note bottom of UC_Revele
La réponse est cachée tant que
l'utilisateur ne la déroule pas
end note
@enduml