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)
220 lines
12 KiB
Markdown
220 lines
12 KiB
Markdown
# Data Dictionary
|
|
|
|
> Dictionnaire de donnees complet du domaine **CFA + Agence d'Acadenice**.
|
|
> Source de verite pour le MCD, MLD et MPD. Mantra BYAN #33 : Data Dictionary First.
|
|
> Scope B valide : entite PERSONNE pivot multi-roles (formateur, developpeur, admin).
|
|
> **Etudiants** : non modelises ici, juste users Docmost.
|
|
|
|
## Conventions
|
|
|
|
- Codes en `snake_case`, prefixes par mnemonique d'entite
|
|
- **Source** : `S` saisi, `C` calcule (rollup/formula), `A` automatique (timestamp/sequence)
|
|
- **Type abstrait** : independant de la techno (mapping Postgres + Baserow plus loin)
|
|
- **Nullable** : `O` oui, `N` non
|
|
|
|
## Mapping types abstrait → Postgres → Baserow
|
|
|
|
| Type abstrait | Postgres | Baserow |
|
|
|---------------|----------|---------|
|
|
| `INT` | `INTEGER` ou `BIGINT` (PK) | `Number` (sans decimal) |
|
|
| `DECIMAL(p,s)` | `NUMERIC(p,s)` | `Number` (avec decimal) |
|
|
| `VARCHAR(n)` | `VARCHAR(n)` | `Text` |
|
|
| `TEXT` | `TEXT` | `Long text` |
|
|
| `DATE` | `DATE` | `Date` |
|
|
| `TIMESTAMPTZ` | `TIMESTAMP WITH TIME ZONE` | `Last modified time` / `Created time` |
|
|
| `ENUM(...)` | `VARCHAR + CHECK` ou type ENUM | `Single select` |
|
|
| `MULTI_ENUM(...)` | tableau VARCHAR ou table associative | `Multiple select` |
|
|
| `EMAIL` | `VARCHAR(254) + CHECK regex` | `Email` |
|
|
| `FK` | `INTEGER + REFERENCES` | `Link to table` |
|
|
|
|
---
|
|
|
|
# Section 1 — Entite pivot
|
|
|
|
## Entite PERSONNE
|
|
|
|
Centrale au modele. Une personne peut cumuler plusieurs roles. Sa capacite annuelle totale se split entre formation et agence.
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `personne_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `personne_nom` | Nom de famille | VARCHAR(100) | N | — | S | trim |
|
|
| `personne_prenom` | Prenom | VARCHAR(100) | N | — | S | trim |
|
|
| `personne_email` | Email pro | EMAIL | N | — | S | UNIQUE, format email |
|
|
| `personne_telephone` | Telephone | VARCHAR(20) | O | NULL | S | format E.164 si rempli |
|
|
| `personne_capacite_annuelle` | Capacite totale heures/an | DECIMAL(6,2) | N | 0 | S | `>= 0` |
|
|
| `personne_split_formation_pct` | Part allouee formation | DECIMAL(4,1) | N | 50.0 | S | `0-100`, `+ split_agence_pct = 100` |
|
|
| `personne_split_agence_pct` | Part allouee agence | DECIMAL(4,1) | N | 50.0 | S | `0-100` |
|
|
| `personne_roles` | Roles cumules | MULTI_ENUM | N | — | S | `formateur \| developpeur \| admin \| direction \| support` |
|
|
| `personne_heures_attribuees_formation` | Cumul heures formation attribuees | DECIMAL(6,2) | N | 0 | C | rollup `SUM(ATTRIBUTION.heures)` |
|
|
| `personne_heures_attribuees_agence` | Cumul heures agence attribuees | DECIMAL(6,2) | N | 0 | C | rollup `SUM(INTERVENTION.heures)` |
|
|
| `personne_heures_restantes_formation` | Capacite formation restante | DECIMAL(6,2) | N | 0 | C | formula |
|
|
| `personne_heures_restantes_agence` | Capacite agence restante | DECIMAL(6,2) | N | 0 | C | formula |
|
|
| `personne_heures_restantes_total` | Capacite totale restante | DECIMAL(6,2) | N | 0 | C | formula |
|
|
| `personne_statut` | Statut | ENUM | N | `actif` | S | `actif \| inactif` |
|
|
|
|
**Formules cles** :
|
|
|
|
```
|
|
personne_heures_restantes_formation = (capacite_annuelle * split_formation_pct / 100) - heures_attribuees_formation
|
|
personne_heures_restantes_agence = (capacite_annuelle * split_agence_pct / 100) - heures_attribuees_agence
|
|
personne_heures_restantes_total = capacite_annuelle - heures_attribuees_formation - heures_attribuees_agence
|
|
```
|
|
|
|
**Note** : un admin pur (pas formateur ni developpeur) peut avoir `capacite_annuelle = 0` et splits a 0.
|
|
|
|
---
|
|
|
|
# Section 2 — Branche CFA
|
|
|
|
## Entite FORMATION
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `formation_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `formation_nom` | Nom | VARCHAR(200) | N | — | S | UNIQUE, trim |
|
|
| `formation_description` | Description | TEXT | O | NULL | S | — |
|
|
| `formation_filiere` | Filiere | ENUM | O | NULL | S | `dev \| graphisme \| marketing \| iot \| cybersec` |
|
|
| `formation_heures_totales` | Heures totales | DECIMAL(6,2) | N | 0 | S | `>= 0` |
|
|
| `formation_heures_attribuees` | Heures attribuees blocs | DECIMAL(6,2) | N | 0 | C | rollup |
|
|
| `formation_heures_restantes` | Restantes | DECIMAL(6,2) | N | 0 | C | formula |
|
|
| `formation_statut` | Statut | ENUM | N | `draft` | S | `draft \| actif \| termine \| archive` |
|
|
| `formation_date_debut` | Date debut | DATE | O | NULL | S | — |
|
|
| `formation_date_fin` | Date fin | DATE | O | NULL | S | `>= date_debut` |
|
|
| `formation_created_at` | Cree le | TIMESTAMPTZ | N | NOW() | A | — |
|
|
| `formation_updated_at` | Modifie le | TIMESTAMPTZ | N | NOW() | A | — |
|
|
|
|
## Entite BLOC
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `bloc_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `bloc_formation_id` | Formation parente | INT FK | N | — | S | FK → FORMATION, CASCADE |
|
|
| `bloc_nom` | Nom | VARCHAR(200) | N | — | S | UNIQUE par formation |
|
|
| `bloc_description` | Description | TEXT | O | NULL | S | — |
|
|
| `bloc_heures_prevues` | Heures du bloc | DECIMAL(6,2) | N | 0 | S | `>= 0` |
|
|
| `bloc_heures_attribuees` | Heures modules | DECIMAL(6,2) | N | 0 | C | rollup |
|
|
| `bloc_heures_restantes` | Restantes | DECIMAL(6,2) | N | 0 | C | formula |
|
|
| `bloc_ordre` | Ordre dans formation | INT | N | 0 | S | `>= 0` |
|
|
|
|
## Entite MODULE
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `module_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `module_bloc_id` | Bloc parent | INT FK | N | — | S | FK → BLOC, CASCADE |
|
|
| `module_nom` | Nom | VARCHAR(200) | N | — | S | trim |
|
|
| `module_description` | Description | TEXT | O | NULL | S | — |
|
|
| `module_heures_prevues` | Heures prevues | DECIMAL(5,2) | N | 0 | S | `>= 0` |
|
|
| `module_heures_attribuees` | Heures attribuees | DECIMAL(5,2) | N | 0 | C | rollup |
|
|
| `module_heures_realisees` | Heures realisees | DECIMAL(5,2) | N | 0 | C | rollup |
|
|
| `module_statut` | Cycle de vie | ENUM | N | `a_attribuer` | S | `a_attribuer \| attribue \| en_cours \| realise \| annule` |
|
|
|
|
## Entite ATTRIBUTION (Module ↔ Personne[formateur])
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `attribution_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `attribution_module_id` | Module attribue | INT FK | N | — | S | FK → MODULE, CASCADE |
|
|
| `attribution_personne_id` | Formateur (Personne) | INT FK | N | — | S | FK → PERSONNE, RESTRICT. Personne doit avoir role `formateur` |
|
|
| `attribution_heures_attribuees` | Heures planifiees | DECIMAL(5,2) | N | 0 | S | `> 0` |
|
|
| `attribution_heures_realisees` | Heures effectuees | DECIMAL(5,2) | N | 0 | S | `>= 0` |
|
|
| `attribution_date_debut` | Debut periode | DATE | O | NULL | S | — |
|
|
| `attribution_date_fin` | Fin periode | DATE | O | NULL | S | `>= date_debut` |
|
|
| `attribution_statut` | Statut | ENUM | N | `planifie` | S | `planifie \| en_cours \| realise \| annule` |
|
|
|
|
---
|
|
|
|
# Section 3 — Branche AGENCE
|
|
|
|
## Entite CLIENT
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `client_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `client_nom` | Nom client | VARCHAR(200) | N | — | S | UNIQUE |
|
|
| `client_contact_principal` | Contact (Nom + role) | VARCHAR(200) | O | NULL | S | — |
|
|
| `client_contact_email` | Email contact | EMAIL | O | NULL | S | format email |
|
|
| `client_contact_telephone` | Telephone | VARCHAR(20) | O | NULL | S | — |
|
|
| `client_secteur` | Secteur d'activite | VARCHAR(100) | O | NULL | S | — |
|
|
| `client_notes` | Notes libres | TEXT | O | NULL | S | — |
|
|
| `client_statut` | Statut | ENUM | N | `prospect` | S | `prospect \| actif \| inactif \| archive` |
|
|
| `client_created_at` | Cree le | TIMESTAMPTZ | N | NOW() | A | — |
|
|
|
|
## Entite PROJET
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `projet_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `projet_client_id` | Client | INT FK | N | — | S | FK → CLIENT, RESTRICT |
|
|
| `projet_nom` | Nom projet | VARCHAR(200) | N | — | S | UNIQUE par client |
|
|
| `projet_description` | Description | TEXT | O | NULL | S | — |
|
|
| `projet_type` | Type | ENUM | O | NULL | S | `site_web \| app_mobile \| api \| infra \| audit \| support \| autre` |
|
|
| `projet_charge_heures` | Charge estimee | DECIMAL(7,2) | N | 0 | S | `>= 0` |
|
|
| `projet_heures_attribuees` | Heures attribuees taches | DECIMAL(7,2) | N | 0 | C | rollup |
|
|
| `projet_heures_realisees` | Heures realisees | DECIMAL(7,2) | N | 0 | C | rollup |
|
|
| `projet_heures_restantes` | Restantes | DECIMAL(7,2) | N | 0 | C | formula |
|
|
| `projet_date_debut` | Date debut | DATE | O | NULL | S | — |
|
|
| `projet_date_fin_prevue` | Date fin prevue | DATE | O | NULL | S | `>= date_debut` |
|
|
| `projet_date_livraison` | Date livraison effective | DATE | O | NULL | S | — |
|
|
| `projet_statut` | Statut | ENUM | N | `devis` | S | `devis \| en_cours \| livre \| cloture \| abandonne` |
|
|
| `projet_formation_id` | Formation pedagogique liee | INT FK | O | NULL | S | FK → FORMATION (lien optionnel pour projets pedagogiques) |
|
|
| `projet_url` | URL livraison | VARCHAR(500) | O | NULL | S | format URL |
|
|
| `projet_repository` | URL repo Git | VARCHAR(500) | O | NULL | S | format URL |
|
|
|
|
## Entite TACHE
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `tache_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `tache_projet_id` | Projet parent | INT FK | N | — | S | FK → PROJET, CASCADE |
|
|
| `tache_titre` | Titre | VARCHAR(200) | N | — | S | trim |
|
|
| `tache_description` | Description | TEXT | O | NULL | S | — |
|
|
| `tache_charge_heures` | Charge estimee | DECIMAL(5,2) | N | 0 | S | `>= 0` |
|
|
| `tache_heures_realisees` | Heures realisees | DECIMAL(5,2) | N | 0 | C | rollup |
|
|
| `tache_priorite` | Priorite | ENUM | O | NULL | S | `faible \| normale \| haute \| critique` |
|
|
| `tache_statut` | Statut | ENUM | N | `todo` | S | `todo \| in_progress \| review \| done \| abandoned` |
|
|
| `tache_date_debut` | Debut prevu | DATE | O | NULL | S | — |
|
|
| `tache_date_fin_prevue` | Fin prevue | DATE | O | NULL | S | `>= date_debut` |
|
|
|
|
## Entite INTERVENTION (Tache ↔ Personne[developpeur])
|
|
|
|
| Code | Designation | Type | Nullable | Default | Source | Contraintes |
|
|
|------|-------------|------|----------|---------|--------|-------------|
|
|
| `intervention_id` | Identifiant | INT | N | seq | A | PK |
|
|
| `intervention_tache_id` | Tache | INT FK | N | — | S | FK → TACHE, CASCADE |
|
|
| `intervention_personne_id` | Developpeur (Personne) | INT FK | N | — | S | FK → PERSONNE, RESTRICT. Personne doit avoir role `developpeur` |
|
|
| `intervention_heures` | Heures effectuees | DECIMAL(5,2) | N | 0 | S | `> 0` |
|
|
| `intervention_date` | Date intervention | DATE | N | TODAY | S | — |
|
|
| `intervention_notes` | Notes / commit ref | TEXT | O | NULL | S | — |
|
|
| `intervention_statut` | Statut | ENUM | N | `realise` | S | `planifie \| realise \| annule` |
|
|
|
|
---
|
|
|
|
# Section 4 — Cardinalites synthetiques
|
|
|
|
| Relation | Source | Cible | Cardinalite |
|
|
|----------|--------|-------|-------------|
|
|
| FORMATION → BLOC | (1,N) | (1,1) | une formation a au moins 1 bloc |
|
|
| BLOC → MODULE | (1,N) | (1,1) | un bloc a au moins 1 module |
|
|
| MODULE ↔ PERSONNE via ATTRIBUTION | (0,N) | (0,N) | n-n porteuse |
|
|
| CLIENT → PROJET | (0,N) | (1,1) | un client peut avoir 0+ projets |
|
|
| PROJET → TACHE | (0,N) | (1,1) | un projet peut etre vide ou avoir des taches |
|
|
| TACHE ↔ PERSONNE via INTERVENTION | (0,N) | (0,N) | n-n porteuse |
|
|
| PROJET ↔ FORMATION | (0,1) | (0,N) | lien optionnel projet pedagogique |
|
|
|
|
# Section 5 — Volumetrie estimee (an 1 / an 5)
|
|
|
|
| Entite | An 1 | An 5 |
|
|
|--------|------|------|
|
|
| PERSONNE | ~30 | ~80 |
|
|
| FORMATION | ~10 | ~50 |
|
|
| BLOC | ~50 | ~250 |
|
|
| MODULE | ~500 | ~2500 |
|
|
| ATTRIBUTION | ~600 | ~3000 |
|
|
| CLIENT | ~10 | ~50 |
|
|
| PROJET | ~20 | ~150 |
|
|
| TACHE | ~200 | ~2000 |
|
|
| INTERVENTION | ~2000 | ~20000 |
|
|
|
|
Volumetrie negligeable cote performance — indexation standard sur les FK suffit.
|