Côté Documentation, un projet Antora
Pour documenter le projet, nous allons utiliser Antora, qui permet la manipulation de la documentation comme si c’était du code source. Cette approche est connue comme Doc as Code. La documentation écrite en Antora respecte le format Asciidoc.
1. Création d’un squelette de projet
Pour générer la documentation d’un projet, Antora attend une structure de dossiers spécifique, où la documentation est organisée en modules et chaque module organise son contenu dans des dossiers différents, appelés une famille.
|
Il est très important de respecter les noms de dossier proposés par Antora et d’utiliser les variables pour faire référence à ces dossiers. Par exemple, le chemin du dossier Un module Antora peut être utilisé dans différents contextes, d’où le besoin de ces variables. |
Création d’un projet Antora à partir de zéro
|
2. Installation d’Antora
Antora est développé en JavaScript et distribué via le gestionnaire de paquets npm, ce qui rend sont installation très simple.
Comme tout paquet npm, vous pouvez l’installer local ou globalement. Ici, nous allons privilégier l’installation locale.
Cette installation vous permettre d’exécuter Antora grâce à la commande npx.
Installation d’Antora sur un dossier local
|
3. Configuration du Playbook
Le Playbook est un fichier de configuration qui pilote la génération d’un site web à partir de la documentation. Il peut être placé à côté de la documentation (comme dans votre projet), ou alors dans un projet indépendant, dédié exclusivement à la génération d’un site web de documentation.
|
4. Configuration du composant Antora QuizMaker-doc
name: quizmaker-doc
title: QuizMaker - Conception
version: 1.0.0
start_page: index.adoc
asciidoc:
attributes:
source-language: asciidoc@
table-caption: false
nav:
- modules/ROOT/nav.adoc
5. Configuration du module ROOT
* xref:index.adoc[]
* xref:composants.adoc[]
* xref:conception.adoc[]
7. La conception des composants
= Spécification des composants
[WARNING]
====
* Les diagrammes présents dans ce document sont exemples, en *aucun cas* ils ne préconisent une solution.
* Pour que votre document soit plus claire, *supprimez* ou commentez les *notes*, *conseils*, *avertissements*, etc.
====
[NOTE]
====
Objectif::
Découpage de la solution en composants (ou sous-systèmes), affectation des responsabilités aux composants et spécification des interfaces fournies et requises par ces composants
Moyens::
Utilisez des diagrammes d'interaction (séquence, communication) pour décrire l'échange de messages entre les composants pour en déduire leurs interfaces.
====
== Vue globale des composants
.Diagramme de composants décrivant la solution proposée
[plantuml]
....
@startuml
frame "My System" {
interface "Interface A" as Web1
interface "Interface B" as Cli1
interface "Interface C" as Web2
interface "Interface D" as Lobby
component "Component A" as GameServer
component "Component L" as LobbyServer
component "Component C" as MiddlewareClient1
component "Component D" as MiddlewareClient2
[Client web 1]
[Client web 2]
}
[LobbyServer] -up-() Lobby
[GameServer] --( Lobby
MiddlewareClient1 --( Lobby
MiddlewareClient2 --( Lobby
[Client web 1] --() Cli1
Cli1 )-- [MiddlewareClient1]
[Client web 2] --( Web2
[MiddlewareClient2] -up-() Web2
[Client web 1] --( Web1
[MiddlewareClient1] -up-() Web1
@enduml
....
[TIP]
====
* Ne multipliez pas les composants{nbsp}! Essayez tout simplement de séparer les concepts liés au métier (questionnaires, questions, etc.) des concepts techniques: WebSockets, REST, etc.
* Le diagramme de composants doit être simple, avec 4 ou 5 composants.
* Ne créez pas de composant pour gérer la persistance, on laissera JPA la gérer.
====
== Composant A
[TIP]
====
* Votre composant ne doit pas vraiment s'appeler *« Composant A »*, c'est juste un exemple !
* Pour spécifier les composants, partez du principe que les clients connaissent le serveur,
mais que le serveur ne connaît pas les clients.
====
.Un autre exemple
[plantuml]
....
@startuml
left to right direction
component A as "Component A" {
portin "WebSockets" as cws
portout "HTTP" as sws
}
interface Provided
interface Required
sws --() Provided
Required )-- cws
@enduml
....
TIP: Remplacez *« Provided »* et *« Required »* par les noms des interfaces fournies et requises par votre composant
=== Responsabilités du Composant A
.Quelques exemples
* Gérer les questionnaires
* Évaluer les copies
=== Interfaces fournies
==== Interface A
[TIP]
====
* Utilisez la notation UML pour spécifier la signature de chaque operation de l'interface.
* Soyez le plus précis possible ! La syntaxe d'UML est bien plus riche que celle de Java, profitez-en{nbsp}!
====
[WARNING]
====
* La syntaxe de la notation UML est différente de celle de Java{nbsp}!
* Les types de base sont différents aussi{nbsp}! Par exemple, `int`, `float`, `bool` *ne sont pas* des types UML.
* Si les paramètres sont multivalués (par ex. ensemble, séquence, etc.) utilisez les cardinalités: `names : String [1..4]`
* `List<>`, `Set<>`, etc. *ne sont pas* des types UML.
====
[plantuml]
....
interface A << Interface >> {
operationA(a: String, b: Integer): Boolean
}
....
TIP: Si nécessaire, utilisez le langage https://sunye.github.io/ocl/[OCL] pour spécifier les pré et post-conditions des opérations.
.Contraintes sur les operations (exemple de spécification en OCL)
[listing, ocl]
....
context A::operationA(a: String, b: Integer): Boolean
pre: a.size() > 3
post:
-- Pas de postconditions
....
==== Interface B
[plantuml]
....
'skinparam ClassHeaderBackgroundColor blue
'skinparam CircleColor blue
'skinparam CircledCharacterFontColor blue
interface C as "C" << (I, White) Interface >> {
operation(a: String, b: Integer): Boolean
}
....
.Contraintes sur les operations
[source, ocl]
....
context B::operationA(a: String, b: Integer): Boolean
pre: a.size() > 3
post:
-- Pas de postconditions
....
== Composant B
[note]
====
Description de ses responsabilités et de ses interfaces
====
=== Interface C
[plantuml]
....
interface GameServer << Interface >> {
connect(pseudo: String, password: String, ip: String, port: Integer):Boolean
createGame(numberOfPlayers : Integer): Integer
join(gameId : Integer): Integer
notifyPlayersReady(): String
chooseRole(role: String, playerId: Integer)
resist(numberOfPawns: Integer)
playPlaceCard(playerId: Integer, cardName: String)
playHuntCard(cardName: String)
letGo()
putToken(tokenName: String, cardNames: String [0..2])
}
....
== Description des opérations
[TIP]
====
* Utilisez les diagrammes d'interaction de la notation UML pour expliquer le comportement des composants et aussi pour valider leurs interfaces.
* Basez-vous sur les cas d'utilisation (spécification des exigences) pour illustrer les interactions entre les différents composants.
* Si une opération a un algorithme plus complexe, utilisez un https://www.uml-diagrams.org/activity-diagrams.html[diagramme d'activités] pour la décrire.
Utilisez les https://www.uml-diagrams.org/activity-diagrams.html#partition[partitions] pour bien décrire le rôle de chaque composant dans l'opération (chaque partition représente soit un composant, soit une interface).
====
[WARNING]
====
* En UML, les interactions se passent au niveau des instances et les activités au niveau des classes{nbsp}!
* Rappel:
** Niveau classes: Classes, opérations, types, etc.
** Niveau instances: Objets, appels d'opération (messages), valeurs, etc.
====
.Connexion au serveur
[plantuml]
....
participant "ClientA:Client" as C1
participant "ClientB:Client" as C2
participant "ClientC:Client" as C3
C1 -> Serveur: Connect("Teddy")
Serveur --> C1 : true
C1 -> Serveur: allPlayers()
Serveur --> C1: {}
C2 -> Serveur: Connect("Romain")
Serveur --> C2 : true
C2 -> Serveur : allPlayers()
Serveur --> C2 : {"Teddy": false}
Serveur --> C1 : newPlayer("Romain")
C3 -> Serveur: Connect("Romain")
Serveur --> C3 : true
Serveur --> C3 : changePseudo("Romain0")
C3 --> Serveur : allPlayers()
Serveur --> C3 : {"Teddy": false, "Romain": false}
Serveur --> C1 : newPlayer("Romain0")
Serveur --> C2 : newPlayer("Romain0")
C1 -> Serveur: ready("Teddy")
Serveur -> C2 : playerReady("Teddy")
Serveur -> C3 : playerReady("Teddy")
C2 -> Serveur: ready("Romain")
Serveur -> C1 : playerReady("Romain")
Serveur -> C3 : playerReady("Romain")
C3 -> Serveur: ready("Romain0")
Serveur -> C1 : playerReady("Romain0")
Serveur -> C2 : playerReady("Romain0")
Serveur -> C1 : play()
Serveur -> C2 : play()
Serveur -> C3 : play()
....
.Déroulement de l'initialisation du tour 1
[plantuml]
....
participant "ClientA:Client" as C1
participant "ClientB:Client" as C2
participant "ClientC:Client" as C3
C1 -> C2 : sync-message()
C1 -> C3 : another-sync-message()
C1 -> C2 : sync-message()
C1 -> C3 : another-sync-message()
C1 -> C2 : sync-message()
C1 -> C3 : another-sync-message()
....
.Déroulement d'un tour, on suppose les clients déjà initialisés et le tour 4
[plantuml]
....
participant "Rom:Client" as C1
participant "Ted:Client" as C2
participant "Isma:Client" as C3
C1 ->> C2 : async-message()
C1 ->> C3 : another-async-message()
....
.Rejoindre une partie
[plantuml]
....
participant "__one:Player__" as player1
participant "__two:Player__" as player2
participant "__three:Player__" as player3
participant "__four:Player__" as player4
participant "__five:Player__" as player5
participant "__six:Player__" as player6
participant "__game:GameServer__" as game
player1 -> game : id := createGame(6)
par
player1 ->> game : playerId := join(id, one)
player2 ->> game : playerId := join(id, two)
player3 ->> game : playerId := join(id, three)
player4 ->> game : playerId := join(id, four)
player5 ->> game : playerId := join(id, five)
player6 ->> game : playerId := join(id, six)
end
game --> player1: notifyPlayersReady()
player1 -> game: role := chooseRole("Creature", two)
par
game --> player1: gameStart(boardGameState)
game --> player2: gameStart(boardGameState)
game --> player3: gameStart(boardGameState)
game --> player4: gameStart(boardGameState)
game --> player5: gameStart(boardGameState)
game --> player6: gameStart(boardGameState)
end
....
8. La conception détaillée
= Conception détaillée des composants
== Travail à réaliser
// ainsi que de décrire comment vous répondez aux différentes exigences non-fonctionnelles.
Objectif::
Spécification détaillée des composants: leur structure
ainsi que le comportement de toutes les opérations de toutes les interfaces fournies par les composants.
+
Pour définir structure de chaque composant, vous devez utiliser un
https://www.uml-diagrams.org/class-diagrams-overview.html[diagramme de classes ](niveau conception) et éventuellement des
https://www.uml-diagrams.org/class-diagrams-overview.html#object-diagram[diagrammes d'objets] et de
https://www.uml-diagrams.org/composite-structure-diagrams.html[structures composite].
+
Le comportement peut-être décrit en utilisant les diagrammes d'activité, d'interaction (diagrammes de séquence et de communication), les machines d'état, ainsi que OCL.
Moyens::
Appliquez les concepts vus en cours: design patterns, principes GRASP, bonnes pratiques, etc.
== Conception détaillée du Composant A
[TIP]
====
* Votre composant ne doit pas vraiment s'appeler *« Composant A »*, c'est juste un exemple !
* La conception doit *impérativement* contenir une ou des classes qui réalisent (implémentent) les interfaces fournies par le Composant
* *N'utilisez pas* des classes comme type d'attributs
* `List<>` *n'est toujours pas* un type UML{nbsp}!
====
=== Diagramme de classes du Composant A
[TIP]
====
Pour élaborer ce diagramme, posez-vous les questions suivantes{nbsp}:
* Quelle(s) classe(s) réalise(nt) les interfaces fournies par le composant{nbsp}?
En d'autres termes, quelles classes jouent le rôle de _contrôleur_{nbsp}?
* Quelle(s) classe(s) joue(nt) le rôle de _façade_{nbsp}?
* Est-il possible d'accéder à toutes les classes du composant, à partir de la _façade_{nbsp}?
En d'autres termes, la _façade_ joue bien son rôle{nbsp}?
====
=== Conception détaillée de l'interface A1
=== Conception détaillée de l'interface A2
=== Conception détaillée de l'interface A3
== Réponses aux exigences non-fonctionnelles
[NOTE]
====
Expliquez dans cette section les répondes aux différentes exigences non-fonctionnelles spécifiées.
====
=== Concurrence
NOTE: TODO!
=== Performance
NOTE: TODO!
=== Interopérabilité
NOTE: TODO!
=== Portabilité
NOTE: TODO!
=== Sécurité
NOTE: TODO!
==== Exigence de sécurité
Notre système doit être sécurisé, même si nous ne manipulons pas des données sensibles. Pour cela nous devons vérifier l'identité de l'utilisateur.
N'ayant pas à nous occuper de l'authentification de l'utilisateur nous admettons que le système s'occupant de cela est correct et lui-même sécurisé. Nous admettons également que, quelle que soit la plateforme utilisée (web, logiciel, application) le service d'authentification sera le même pour tous.
=== Maintenanbilité
NOTE: TODO!
==== Maintenabilité
NOTE: TODO!
=== Interface utilisateur
NOTE: TODO!
=== Interface logicielle
NOTE: TODO!
=== Interface ou protocoles de communication
NOTE: TODO!
=== Correction
NOTE: TODO!
== Patrons logiciels utilisés
NOTE: Décrivez dans cette partie les patrons logiciels utilisés pour mettre en œuvre l'application.
=== Patron de conception "A"
NOTE: TODO!
=== Patron architectural "B"
NOTE: TODO!
== Choix techniques - Distribution des processus
[NOTE]
====
Explicitez les différents choix techniques et les réponses technologiques aux différentes contraintes que le système implique.
====
Pour cela nous allons donc vous présenter l'environnement général de développement puis énoncer les 4 contraintes que nous avons déterminées de notre logiciel.
Nous avons fais le choix d'utiliser comme environnement de travail l'IDE eclipse.
Pour la raison que nous connaissons tous très bien cette environnement, ce qui nous permet d'avoir tous le même environnement de développement.
Également, cette IDE permet la gestion d'un projet maven ce qui nous sera parfaitement adapté.
Voici les 4 contraintes que nous avons déterminées :
. L'interface graphique.
. La communication vers la base de données.
. La communication entre les machines.
. La sécurité.