Conception complete (Phase 0) pour formation-hub Acadenice : - 19 docs Merise Agile + UML + GitOps + plans (tests/deploy/ops/api) cf docs/00-readme.md pour l'index complet - Stack Docker compose (Docmost + Baserow + Postgres + Redis + MinIO local FS) compose.yml + compose.staging.yml + compose.prod.yml - CI/CD GitHub Actions skeleton (ci, deploy-staging, deploy-prod) - Bridge service skeleton (Hono + TS + Biome + Vitest + zod + pino) - Templates GitHub : PR + 3 issue types + CODEOWNERS + dependabot.yml - Scripts ops : healthcheck, backup quotidien, smoke-test post-deploy - LICENSE AGPL-3.0 + SECURITY.md + CONTRIBUTING.md + CHANGELOG.md - Diagramme drawIO archi infra (XML importable dans diagrams.net) Decisions structurelles enregistrees : - Scope CFA + Agence avec entite PERSONNE pivot multi-roles (ADR-001) - Stack composite Docmost AGPL + Baserow MIT + bridge custom (ADR-001) - Path B : UX quasi-unified via Tiptap node-views custom (ADR-002) - Monorepo trunk-based development (ADR-003) - Postgres separe Docmost/Baserow (ADR-004) - Bridge stack Node 22 + Hono (ADR-005) - Repo neuf prefere a fork Docmost - Prod-like des le jour 1 (pas MVP)
8.6 KiB
8.6 KiB
UML Class Diagram
Vue orientee objet du modele. Scope B (CFA + Agence + Personne pivot). Apporte les methodes que le MCD ne montre pas. Pont entre modele de donnees et code du bridge service Phase 2.
1. Pourquoi un class diagram en plus du MCD
Le MCD montre les donnees (entites + attributs + relations). Le class diagram montre :
- Les methodes sur chaque classe
- La visibilite (public/private/protected)
- Les types de relations OO (composition, agregation, association)
- Les patterns applicables
2. Diagrammes par zone
Splitte en 3 sous-vues : CFA, Agence, Personne pivot. Plus un diagramme global simplifie pour la vue d'ensemble.
2.1 Vue globale (relations seules)
classDiagram
Personne -- Attribution : "role formateur"
Personne -- Intervention : "role developpeur"
Formation *-- Bloc
Bloc *-- Module
Module -- Attribution
Client -- Projet
Projet *-- Tache
Tache -- Intervention
Projet -- Formation : "projet pedagogique"
2.2 Zone CFA — classes detaillees
classDiagram
class Formation {
+int id
+string nom
+Filiere filiere
+decimal heuresTotales
-decimal heuresAttribuees$
+Statut statut
+activer() void
+archiver() void
+ajouterBloc(Bloc) void
+heuresRestantes() decimal
+rapportPDF() Buffer
}
class Bloc {
+int id
+string nom
+decimal heuresPrevues
-decimal heuresAttribuees$
+ajouterModule(Module) void
+heuresRestantes() decimal
}
class Module {
+int id
+string nom
+decimal heuresPrevues
-decimal heuresAttribuees$
-decimal heuresRealisees$
+Statut statut
+creerAttribution(Personne, decimal, Date, Date) Attribution
+annuler() void
+cloturer() void
}
class Attribution {
+int id
+decimal heuresAttribuees
+decimal heuresRealisees
+Statut statut
+demarrer() void
+saisirHeuresRealisees(decimal) void
+cloturer() void
+annuler(string) void
}
Formation "1" *-- "1..*" Bloc : composition
Bloc "1" *-- "1..*" Module : composition
Module "1" -- "0..*" Attribution : association
2.3 Zone Agence — classes detaillees
classDiagram
class Client {
+int id
+string nom
+string contactPrincipal
+Email contactEmail
+Statut statut
+creerProjet(string) Projet
+archiver() void
}
class Projet {
+int id
+string nom
+Type type
+decimal chargeHeures
-decimal heuresRealisees$
+Statut statut
+ajouterTache(string, decimal) Tache
+lierFormationPedagogique(Formation) void
+livrer() void
+cloturer() void
+rapportPDF() Buffer
}
class Tache {
+int id
+string titre
+decimal chargeHeures
-decimal heuresRealisees$
+Priorite priorite
+Statut statut
+creerIntervention(Personne, decimal, Date) Intervention
+marquerInProgress() void
+marquerReview() void
+marquerDone() void
}
class Intervention {
+int id
+decimal heures
+Date date
+Statut statut
+annuler(string) void
}
Client "1" -- "1..*" Projet : association
Projet "1" *-- "0..*" Tache : composition
Tache "1" -- "0..*" Intervention : association
2.4 Zone Personne pivot — classe + roles
classDiagram
class Personne {
+int id
+string nom
+string prenom
+Email email
+decimal capaciteAnnuelle
+decimal splitFormationPct
+decimal splitAgencePct
+Set~Role~ roles
+Statut statut
-decimal heuresAttribueesFormation$
-decimal heuresAttribueesAgence$
+heuresRestantesFormation() decimal
+heuresRestantesAgence() decimal
+heuresRestantesTotal() decimal
+ajouterRole(Role) void
+retirerRole(Role) void
+activer() void
+inactiver() void
+rapportPDF() Buffer
}
class Attribution {
+int id
+decimal heuresAttribuees
+Statut statut
}
class Intervention {
+int id
+decimal heures
+Statut statut
}
Personne "1" -- "0..*" Attribution : "role formateur"
Personne "1" -- "0..*" Intervention : "role developpeur"
2.5 Lien pedagogique cross-zone
classDiagram
class Projet {
+int id
+string nom
+lierFormationPedagogique(Formation) void
}
class Formation {
+int id
+string nom
}
Projet "0..*" -- "0..1" Formation : "projet pedagogique"
Notation :
+public,-private,#protected$champ derive/calcule (rollup ou formula, pas stocke directement)*--composition (cycle de vie partage)--association simple
3. Methodes detaillees — Personne
| Methode | Signature | Description |
|---|---|---|
heuresRestantesFormation() |
decimal |
(capacite * split_formation_pct/100) - heures_attribuees_formation |
heuresRestantesAgence() |
decimal |
(capacite * split_agence_pct/100) - heures_attribuees_agence |
heuresRestantesTotal() |
decimal |
capacite - heures_attribuees_formation - heures_attribuees_agence |
ajouterRole(role) |
Role → void |
Ajoute role aux roles existants. Idempotent. |
retirerRole(role) |
Role → void |
Retire role. Verifie qu'aucune attribution/intervention active n'utilise ce role. |
activer() |
void |
Statut → actif |
inactiver() |
void |
Statut → inactif. Bloque nouvelles assignations. |
4. Methodes detaillees — Module / Tache
Module.creerAttribution(personne, heures, dateDebut, dateFin)
Verifications :
personne.roles.contains(Role.formateur)— sinon throwRG-01:SUM(this.attributions.heures) + heures <= this.heuresPrevues- Warning si
heures > personne.heuresRestantesFormation()
Effet : INSERT attribution + recalcul rollups en cascade.
Tache.creerIntervention(personne, heures, date)
Verifications :
personne.roles.contains(Role.developpeur)— sinon throwheures > 0personne.statut == actif
Effet : INSERT intervention + recalcul rollups (tache, projet, personne).
5. Patterns OO appliques
| Pattern | Ou | Pourquoi |
|---|---|---|
| Value Object | Email, Decimal heures, Filiere, Type, Priorite | Immutables, validation a la construction |
| State Pattern | Statut sur toutes les entites avec cycle de vie | Encapsule transitions valides |
| Repository | PersonneRepo, ProjetRepo, etc. | Abstrait l'acces Baserow API |
| Factory | Module.creerAttribution(), Tache.creerIntervention() | Encapsule logique creation + validations |
| Observer | Webhooks Baserow → bridge listeners | Evenements rollup → recalculs |
| Strategy | Calcul capacite Personne (split formation/agence) | Permet de varier la regle (ex: split par periode) |
6. Mapping vers le code du bridge service (Phase 2)
// bridge/src/domain/personne.ts
import { Decimal } from 'decimal.js';
export type Role = 'formateur' | 'developpeur' | 'admin' | 'direction' | 'support';
export class Personne {
constructor(
public readonly id: number,
public nom: string,
public prenom: string,
public email: string,
public capaciteAnnuelle: Decimal,
public splitFormationPct: Decimal,
public splitAgencePct: Decimal,
public roles: Set<Role>,
public statut: 'actif' | 'inactif',
private _heuresAttribueesFormation: Decimal,
private _heuresAttribueesAgence: Decimal,
) {
if (!this.splitFormationPct.plus(this.splitAgencePct).equals(100)) {
throw new Error('Splits doivent sommer a 100');
}
}
heuresRestantesFormation(): Decimal {
const alloue = this.capaciteAnnuelle.times(this.splitFormationPct).div(100);
return alloue.minus(this._heuresAttribueesFormation);
}
heuresRestantesAgence(): Decimal {
const alloue = this.capaciteAnnuelle.times(this.splitAgencePct).div(100);
return alloue.minus(this._heuresAttribueesAgence);
}
heuresRestantesTotal(): Decimal {
return this.capaciteAnnuelle.minus(this._heuresAttribueesFormation).minus(this._heuresAttribueesAgence);
}
// ...
}
7. Limites du class diagram
- Ne montre pas la persistence (deja fait par MLD)
- Ne montre pas les sequences (deja fait par UML use cases sequence diagrams)
- Redondant avec MCD sur les attributs simples
C'est intentionnel : chaque vue eclaire un angle different. Le class diagram = angle comportemental statique cote code.