27 KiB
Design Document
Spec : Socle Plateforme SchotzimmO
Document de conception technique rédigé en français ; les titres de sections suivent le gabarit standard. Le besoin fonctionnel métier est décrit dans
requirements.md; les exigences techniques dérivées dansdesign-requirements.md. La présente conception met en œuvre ces exigences.
Overview
SchotzimmO (« l'ami de l'immobilier » ; Schotzi signifie « ami » en alsacien, immO pour l'immobilier) est une plateforme web SaaS multi-utilisateur, multi-réseaux et multi-module destinée aux mandataires immobiliers. Ce document décrit la conception technique du socle : les capacités transverses communes à tous les Modules (authentification, comptes, Réseaux, parrainage/hiérarchie, visibilité descendante, annuaire, abonnements/Packs, activation des Modules, administration, tableau de bord, cadre applicatif, isolation des données).
Le socle fournit l'ossature sur laquelle se branchent les Modules métier (Carnet d'adresses, KMS, CRM, Agenda, Facturation…), chacun décrit dans sa propre spec. Il définit les contrats transverses que ces Modules implémentent (contribution au Tableau_De_Bord, partage hiérarchique, contrôle d'accès par Abonnement).
Décision structurante : architecture à deux niveaux de base
L'isolation des réseaux est physique, par une architecture à deux niveaux de base de données :
- Base Socle (centrale, unique) : authentification, référentiel des Réseaux et leurs informations de connexion, abonnements, catalogue des Modules/Packs, administration, KPI d'usage remontés par réseau. Elle ne contient aucune donnée métier produite par les mandataires.
- Base Réseau (une par Réseau) : toutes les données métier cloisonnées — profil mandataire, parrainage/hiérarchie, et données des Modules.
À la connexion, le socle authentifie l'utilisateur sur la Base Socle, lit son Réseau, en déduit la chaîne de connexion de sa Base Réseau, et toute l'activité métier de la session s'exécute sur cette Base Réseau. L'identité (account_id) est générée côté socle et réutilisée comme clé du profil côté réseau (identité partagée).
Périmètre fonctionnel (socle)
| # | Capacité | Description |
|---|---|---|
| 1 | Inscription | Création de Compte avec Reseau, Identifiant_Reseau, Statut_Juridique, SIREN optionnel ; via Lien_Parrainage ou directe ; Starter par défaut |
| 2 | Authentification & session | Login e-mail/mot de passe (Base Socle), JWT, « se souvenir de moi », reset par e-mail |
| 3 | Réseaux | Référentiel multi-réseaux, base par réseau, onboarding réseau par l'Administrateur, personnalisation (thème/paramétrage) |
| 4 | Parrainage & hiérarchie | Déclaration parrain/filleuls, invitations e-mail, états supposé/validé, rapprochement par e-mail, confirmation bilatérale, anti-cycle (Base Réseau) |
| 5 | Visibilité descendante | Agrégats sur toute la Descendance, détail limité aux Filleuls_Directs (N+1) |
| 6 | Annuaire | Liste et arbre des membres d'un Reseau (inscrits + déclarés), recherche |
| 7 | Abonnements & Packs | Starter/Pro/Premium inclusifs, souscription, rétrogradation = conservation + consultation seule |
| 8 | Catalogue & activation Modules | Référentiel Module->Pack, états accessible/consultation/verrouillé, contrôle d'accès par appel |
| 9 | Administration | Rôle admin, gestion comptes/abonnements/catalogue, onboarding réseau, correction d'arbre, KPI d'usage par réseau |
| 10 | Tableau de bord | Agrégation résiliente des contributions des Modules accessibles |
| 11 | Cadre applicatif | Navigation dynamique, avatar/profil, mapping d'erreurs, garde d'auth, personnalisation par réseau |
| 12 | Isolation des données | Isolation physique par Base Réseau + authentification centrale ; références inter-bases logiques |
Contraintes techniques
- Multi-utilisateur authentifié : chaque Mandataire possède un Compte (Base Socle) ; session par JWT signé HS256 (durée configurable, défaut 720 min / 12 h) ; tous les endpoints de données sont protégés par authentification Bearer.
- Isolation physique par réseau : une Base Réseau par Réseau ; la Base Socle ne porte aucune donnée métier. Les références
account_identre Base Socle et Base Réseau sont logiques (mêmes valeurs d'identifiant), jamais des FK SQL ni des jointures inter-bases. - Résolution de connexion par réseau : la Base Socle stocke, par Réseau, une clé logique (
db_connection_key) qui résout la chaîne de connexion de la Base Réseau via la configuration (jamais la chaîne en base). Un service de résolution fournit leDbContextréseau de la requête selon le claimnetworkdu JWT. - Frontend : Angular (TypeScript), SPA, routage protégé par un garde d'authentification, navigation construite dynamiquement depuis le catalogue des Modules, thème/libellés personnalisables par Réseau.
- Backend : ASP.NET Core Web API (C#), architecture en couches (Controllers -> Services -> Repositories/DbContext) avec deux DbContext :
SocleDbContextetReseauDbContext. - Base de données : MariaDB / MySQL, via le provider EF Core Pomelo.EntityFrameworkCore.MySql. Chaînes de connexion fournies par variable d'environnement : la Base Socle et chaque Base Réseau peuvent être conteneurisées ou externes (cas de l'environnement de PROD de l'exploitant). Migrations EF Core appliquées de façon idempotente : schéma socle sur la Base Socle, schéma réseau sur chaque Base Réseau.
- E-mails : service SMTP configurable (invitations, réinitialisation). Si
Smtp:Hostest vide, repli de journalisation du lien. - Hébergement : Docker Compose — un conteneur
frontend(Angular servi par nginx) publié sur un port hôte, un conteneurbackend(ASP.NET Core) non exposé directement, et des bases MariaDB conteneurisées (services avec volumes) ou externes. Terminaison HTTPS par un reverse-proxy externe (ex. Caddy). - Deux réseaux au lancement :
iad(production) etdemo(démonstration en ligne + tests de développement). - Politique de visibilité :
403 Forbiddenpour tout accès hors Portee_Hierarchique ;404pour les ressources réellement inexistantes dans le périmètre autorisé.
Charte graphique
Reprise de la palette SchotzimmO : variables CSS globales — Orange #F28C28, Bleu #0074B8 (primaire), Anthracite #343A40, Gris clair #E7E9EA. Le bleu est conservé comme clin d'oeil au réseau IAD (réseau partenaire au lancement). La charte de base est personnalisable par Réseau (thème, libellés) via le paramétrage porté par la Base Socle. Convention d'interaction : les boutons d'action passent à l'orange plein au survol ; les boutons « Annuler » restent neutres, les suppressions rouges, les états actifs en bleu plein. Les assets (logo, favicon) seront fournis ultérieurement.
Architecture
Architecture globale
graph TB
subgraph "Navigateur (SPA)"
FE[Angular App<br/>TypeScript]
end
subgraph "Backend (ASP.NET Core)"
API[API REST<br/>Controllers]
AUTHZ[Autorisation<br/>Auth + Abonnement + Hierarchie]
RES[NetworkConnectionResolver<br/>resout la base reseau]
SVC[Services socle + modules]
MOD[Contrats Modules<br/>IModuleAccess<br/>IDashboardContributor<br/>IHierarchySharing]
MAIL[EmailService<br/>SMTP / repli log]
SOCLE[(SocleDbContext)]
RESEAU[(ReseauDbContext)]
end
subgraph "Stockage"
DBS[(Base Socle<br/>MariaDB)]
DBR1[(Base Reseau IAD)]
DBR2[(Base Reseau Demo)]
end
SMTP[Serveur SMTP]
FE -- "HTTP/JSON + Bearer JWT" --> API
API --> AUTHZ
AUTHZ --> RES
AUTHZ --> SVC
SVC --> MOD
SVC --> MAIL
SVC --> SOCLE
SVC --> RESEAU
SOCLE --> DBS
RES -. "chaine de connexion selon claim network" .-> RESEAU
RESEAU --> DBR1
RESEAU --> DBR2
MAIL -- "SMTP" --> SMTP
Flux de connexion et résolution de base réseau
sequenceDiagram
participant U as Utilisateur
participant FE as Angular Frontend
participant API as AuthController
participant SOCLE as SocleDbContext (Base Socle)
participant RES as NetworkConnectionResolver
participant RESEAU as ReseauDbContext (Base Reseau)
U->>FE: Saisit e-mail + mot de passe
FE->>API: POST /api/auth/login
API->>SOCLE: Verifie identifiants + lit network_id
SOCLE-->>API: Compte valide (account_id, network, role)
API-->>FE: JWT { sub, network, role }
U->>FE: Requete metier (Bearer JWT)
FE->>API: GET /api/... (module)
API->>RES: Resout la connexion pour claim network
RES-->>RESEAU: Chaine de connexion de la Base Reseau
API->>RESEAU: Execute la requete metier (account_id)
RESEAU-->>FE: Donnees du reseau
Flux de contrôle d'accès à un Module
sequenceDiagram
participant FE as Frontend
participant API as Module Controller
participant AUTHZ as Autorisation
participant SUB as SubscriptionService (Base Socle)
participant SVC as Service du Module (Base Reseau)
FE->>API: Requete Module + Bearer JWT
API->>AUTHZ: Verifie JWT (sub, network, role)
AUTHZ->>SUB: Droit sur ce Module ?
SUB-->>AUTHZ: accessible | consultation | verrouille
alt verrouille
AUTHZ-->>FE: 403 (abonnement requis)
else consultation + ecriture
AUTHZ-->>FE: 403 (lecture seule)
else autorise
AUTHZ->>SVC: Execute sur la Base Reseau (account_id)
SVC-->>FE: 200 / 201
end
Data Models
Le modèle est réparti sur deux bases. Le détail physique (types MariaDB, index, contraintes, seed) est tenu dans .kiro/specs/_data-model/database.md.
Base Socle (centrale)
erDiagram
NETWORK ||--o{ ACCOUNT : "network_id"
NETWORK ||--o{ NETWORK_USAGE_KPI : "network_id"
ACCOUNT ||--|| SUBSCRIPTION : "account_id"
PACK ||--o{ SUBSCRIPTION : "pack_id"
PACK ||--o{ MODULE_CATALOG : "required_pack_id"
ACCOUNT ||--o{ PASSWORD_RESET : "account_id"
NETWORK {
int Id PK
string Code UK
string Name UK
string DbConnectionKey UK
text ThemeSettings "nullable"
bool IsActive
bool IsProvisioned
}
ACCOUNT {
int Id PK
int NetworkId FK
string Email UK
string PasswordHash
string Role "mandataire|admin"
bool IsActive
datetime CreatedAt
}
PACK {
int Id PK
string PackKey UK
int Level UK
bool IsFree
}
MODULE_CATALOG {
int Id PK
string ModuleKey UK
int RequiredPackId FK
bool IsActive
}
SUBSCRIPTION {
int Id PK
int AccountId FK
int PackId FK
datetime StartsAt
datetime EndsAt "nullable"
string Status "actif|expire"
}
PASSWORD_RESET {
int Id PK
int AccountId FK
string TokenHash
datetime ExpiresAt
bool Used
}
NETWORK_USAGE_KPI {
int Id PK
int NetworkId FK
string MetricKey
decimal MetricValue
string Period "nullable"
}
Base Réseau (une par réseau)
erDiagram
MANDATAIRE_PROFILE ||--o| MANDATAIRE_PROFILE : "sponsor_account_id"
MANDATAIRE_PROFILE ||--o{ DECLARED_MEMBER : "declared_by_account_id"
MANDATAIRE_PROFILE ||--o{ AFFILIATION_REQUEST : "requester / target"
MANDATAIRE_PROFILE {
int AccountId PK "= accounts.id du socle (ref logique)"
string FirstName
string LastName
string NetworkMemberId UK
string LegalStatus "nullable"
string Siren "nullable"
int SponsorAccountId FK "nullable"
datetime CreatedAt
}
DECLARED_MEMBER {
int Id PK
string Email
string FirstName "nullable"
string LastName "nullable"
int DeclaredByAccountId FK
string Relation "parrain|filleul"
string Status "declare|invite"
}
AFFILIATION_REQUEST {
int Id PK
int RequesterAccountId FK
int TargetAccountId FK
string ProposedRelation "parrain|filleul"
string Status "en_attente|approuvee|refusee"
}
Notes de modélisation
- Identité partagée (Option B) :
ACCOUNT(Base Socle) porte le strict minimum d'authentification.MANDATAIRE_PROFILE(Base Réseau) porte le profil métier et le parrainage, sa clé primaireAccountIdréutilisantaccounts.id. La cohérence des deux morceaux (création, désactivation) est assurée applicativement. - Parrainage dans la Base Réseau : l'auto-référence
sponsor_account_idest une vraie FK au sein de la base réseau -> intégrité du parrainage garantie par la base. Le cloisonnement réseau est physique (base propre au réseau), d'où l'absence de colonnenetwork_iddans les tables réseau. - Références inter-bases :
account_idcircule entre les deux bases comme référence logique (FK*), jamais via une FK SQL ou une jointure traversant deux bases. - Abonnement : un seul
SUBSCRIPTIONactif par Compte (Base Socle). Packs inclusifs parLevel. Consultation seule déduite : Module hors Pack courant mais données présentes dans la Base Réseau. - Réseau :
networks.db_connection_key= clé logique de connexion (secret en configuration) ;is_provisionedindique que la Base Réseau est initialisée (schéma + seed) avant toute inscription. - KPI d'usage :
network_usage_kpisreçoit des agrégats non nominatifs remontés par chaque réseau ; aucune consolidation ne traverse les bases réseau.
Components and Interfaces
Endpoints d'API (socle)
| Domaine | Méthode & route | Rôle |
|---|---|---|
| Réseaux | GET /api/networks |
Liste des Reseaux ouverts à l'inscription (public) |
| Inscription | POST /api/auth/register |
Création de Compte (directe ou via referralToken) |
| Auth | POST /api/auth/login |
Connexion (Base Socle), retourne le JWT |
| Auth | POST /api/auth/forgot-password |
Déclenche la réinitialisation (réponse constante) |
| Auth | POST /api/auth/reset-password |
Applique le nouveau mot de passe via jeton |
| Auth | POST /api/auth/change-password |
Changement de mot de passe (authentifié) |
| Profil | GET /api/profile, PUT /api/profile |
Consulter/modifier le Profil (Base Réseau ; Reseau non modifiable) |
| Parrainage | GET /api/referrals/validate |
Valide un Lien_Parrainage et retourne le pré-remplissage |
| Parrainage | POST /api/referrals/declare |
Déclarer parrain / filleuls |
| Parrainage | POST /api/referrals/{id}/invite |
Envoyer une Invitation par e-mail |
| Parrainage | POST /api/referrals/confirmations |
Proposer une Confirmation_Affiliation |
| Parrainage | POST /api/referrals/confirmations/{id}/approve|reject |
Approuver / refuser |
| Annuaire | GET /api/directory |
Membres du Reseau (inscrits + déclarés), paginé, recherche |
| Annuaire | GET /api/directory/tree |
Arbre de la Descendance de l'appelant |
| Abonnement | GET /api/subscription, POST /api/subscription |
Abonnement courant / changement de Pack |
| Modules | GET /api/modules |
Catalogue + état par Module (accessible/consultation/verrouille) |
| Tableau de bord | GET /api/dashboard |
Agrégation des contributions des Modules accessibles |
| Admin | GET /api/admin/accounts, PUT /api/admin/accounts/{id}/subscription |
Gestion des comptes/abonnements |
| Admin | POST /api/admin/networks, PUT /api/admin/networks/{id} |
Onboarding et paramétrage d'un Reseau |
| Admin | PUT /api/admin/modules |
Rattachement Module -> Pack |
| Admin | GET /api/admin/stats |
KPI d'usage remontés par réseau (non nominatifs) |
| Admin | POST /api/admin/referrals/reassign |
Correction de l'arbre de parrainage |
| Santé | GET /health |
Sonde de disponibilité (healthcheck conteneur) |
Composants backend structurants
- SocleDbContext — accès à la Base Socle (networks, accounts, packs, module_catalog, subscriptions, password_resets, network_usage_kpis).
- ReseauDbContext — accès à une Base Réseau (mandataire_profiles, declared_members, affiliation_requests, + tables des Modules). Sa chaîne de connexion est fournie à la requête.
- NetworkConnectionResolver — à partir du claim
networkdu JWT, résout ladb_connection_keydu Réseau vers la chaîne de connexion (configuration) et fournit leReseauDbContextde la requête. - AuthService / AccountService — authentification sur la Base Socle, création de Compte (socle) + profil (réseau) dans une opération coordonnée.
- ReferralService — déclarations, invitations, rapprochement par e-mail, confirmations (Base Réseau).
- SubscriptionService — abonnement et calcul des Modules accessibles / consultation seule.
- NetworkProvisioningService — onboarding d'un Réseau (création/initialisation de la Base Réseau : schéma + seed), réservé à l'Administrateur.
Contrats transverses pour les Modules
Approche « plugins applicatifs » par injection de dépendances (composition à la compilation) :
- IModuleAccess — déclare le
ModuleKeyet leRequiredPackd'un Module ; permet au socle de calculer l'état (accessible/consultation/verrouille) et de garder les routes. - IDashboardContributor — fournit les éléments d'un Module pour le Tableau_De_Bord, appelé de façon résiliente.
- IHierarchySharing — fournit, pour un périmètre hiérarchique, les agrégats (toute la Descendance) et le détail N+1 (Filleuls_Directs) que le Module accepte de partager.
Ajouter un Module = enregistrer son implémentation (injection de dépendances), son entrée de Catalogue_Modules, et ses tables dans le schéma de la Base Réseau.
Error Handling
- Mapping des Codes_HTTP (centralisé côté Frontend, messages en français) :
401(session expirée -> reconnexion),403(accès refusé / abonnement requis / lecture seule),404(ressource inexistante dans le périmètre autorisé),409(conflit : e-mail déjà utilisé, cycle de parrainage),422(erreurs de validation),5xx(indisponibilité). - Résolution de base réseau : si la Base Réseau d'un Réseau est indisponible ou non provisionnée, le Backend répond
503(service indisponible) sans exposer de détail d'infrastructure ; la connexion n'est jamais tentée pour un Réseauis_provisioned = 0. - Validation d'entrée : DTO validés côté Backend ; erreurs de champ en
422, sans divulgation sensible. - Anti-énumération :
forgot-passwordrépond toujours200;registerrenvoie un message générique si l'e-mail est déjà pris. - Isolation en cas d'erreur d'accès : accès hors Portee_Hierarchique ->
403;404possible pour les ressources dont l'existence serait sensible. - Cohérence inter-bases : la création d'un Compte (socle) et de son profil (réseau) est coordonnée ; en cas d'échec de l'une des étapes, l'opération est annulée pour ne pas laisser un Compte sans profil (ou l'inverse).
- Transactions : toute écriture multi-entités au sein d'une base est atomique (rollback complet en cas d'échec). Les opérations traversant les deux bases sont séquencées avec compensation applicative (pas de transaction distribuée).
- Secrets : aucun secret (mot de passe, token, chaîne de connexion) n'est journalisé ni renvoyé dans un message d'erreur.
- Résilience du Tableau de bord : l'échec de la contribution d'un Module est isolé et n'empêche pas le rendu des autres sections.
Testing Strategy
- Tests unitaires (Backend) : services socle (authentification, création compte+profil coordonnée, calcul des Modules accessibles, portée hiérarchique), résolution de connexion réseau (mock de configuration), avec mocks (SMTP, horloge).
- Tests d'intégration (Backend) : endpoints d'API sur une Base Socle + une Base Réseau de test (conteneurs éphémères), vérifiant les Codes_HTTP, l'isolation par base réseau et la cohérence inter-bases.
- Tests de propriétés / invariants : non-existence de cycle dans l'Arbre_Parrainage, au plus un abonnement actif par compte, visibilité strictement descendante, aucune jointure/accès traversant deux bases réseau.
- Tests Frontend : composants Angular clés (formulaires d'inscription/connexion, navigation dynamique, personnalisation par réseau, mapping d'erreurs).
- Tests de sécurité ciblés : routes protégées refusant l'accès sans JWT valide, routes admin refusant un rôle
mandataire, impossibilité d'accéder à une Base Réseau autre que celle du claimnetwork. - Non-régression : suite exécutée au build ; aucune livraison sans build vert.
Correctness Properties
Propriétés que le système doit garantir en permanence (invariants vérifiables) :
Property 1: Isolation physique par réseau
Validates: Requirements 3.3, 3.4, 12.2, 12.5
Toute donnée métier d'un Réseau réside dans sa Base Réseau ; aucune requête ne lit les données d'un autre Réseau, et aucune jointure ne traverse deux bases.
Property 2: Authentification centrale cohérente
Validates: Requirements 1.3, 2.2, 12.1
Un Compte existe dans la Base Socle avec un e-mail unique sur toute la plateforme ; tout Compte actif possède un profil correspondant dans sa Base Réseau (account_id partagé), et réciproquement.
Property 3: Acyclicité du parrainage
Validates: Requirements 4.5, 4.6
L'Arbre_Parrainage ne contient aucun cycle ; un mandataire a au plus un sponsor_account_id validé.
Property 4: Visibilité descendante stricte
Validates: Requirements 5.1, 5.2, 5.3, 5.4
Un mandataire n'accède jamais aux données d'un ancêtre ou d'une autre branche ; le détail est limité au niveau N+1, l'agrégat couvre toute la Descendance.
Property 5: Unicité de l'abonnement actif
Validates: Requirements 7.7
Au plus un abonnement au statut actif par Compte à un instant donné (Base Socle).
Property 6: Inclusivité des Packs
Validates: Requirements 7.2, 8.1
Un Compte accède à tout Module dont RequiredPack.Level <= Level du Pack courant.
Property 7: Conservation à la rétrogradation
Validates: Requirements 7.5, 7.6, 7.8
Aucune donnée n'est supprimée lors d'une Retrogradation ; les Modules hors Pack mais pourvus de données (Base Réseau) restent accessibles en consultation seule.
Property 8: Confidentialité des secrets
Validates: Requirements 2.7, 2.8
Les mots de passe, jetons et chaînes de connexion ne sont jamais stockés en clair ni journalisés ; la Base Socle ne stocke qu'une clé logique de connexion.
Property 9: Atomicité et cohérence inter-bases
Validates: Requirements 12.4
Toute écriture multi-entités au sein d'une base est atomique ; les opérations traversant les deux bases sont compensées pour ne jamais laisser un Compte sans profil (ou l'inverse).
Property 10: Réseau provisionné avant inscription
Validates: Requirements 3.1, 3.2
Aucune inscription n'est possible sur un Réseau non actif ou non provisionné (is_active = 0 ou is_provisioned = 0).
Design Decisions
- Deux niveaux de base (Base Socle + Base Réseau par réseau) (retenu) vs base unique mutualisée avec cloisonnement applicatif (rejeté) : l'isolation physique par réseau garantit le cloisonnement par construction (pas de fuite inter-réseaux par bug), facilite la personnalisation par réseau et une éventuelle exigence contractuelle de séparation. Coût assumé : migrations à appliquer sur N bases réseau, provisioning d'une base à chaque nouveau réseau, et références inter-bases logiques (pas de FK SQL). Alternative mutualisée écartée car le projet valorise l'isolation forte et la personnalisation par réseau.
- Identité partagée, Option B (retenu) vs compte entièrement au socle (rejeté) : l'authentification minimale reste centrale (login unique), mais le profil et le parrainage vivent dans la Base Réseau pour que les FK métier (parrainage, propriété des données) restent intra-base et garanties par MariaDB. Le compte entièrement au socle aurait dispersé les FK métier entre deux bases, sans intégrité référentielle.
- Clé logique de connexion + secret en configuration (retenu) vs chaîne de connexion stockée en base (rejeté) : conforme à la règle « aucun secret en base/log » ; la Base Socle ne contient qu'une clé, le secret restant dans la configuration d'environnement.
- Pas de consolidation inter-réseaux ; KPI remontés par réseau (retenu) : l'administration consomme des agrégats non nominatifs poussés par chaque réseau dans
network_usage_kpis, sans requête traversant les bases réseau. Évite toute jointure inter-bases et respecte le cloisonnement. - MariaDB/MySQL via Pomelo (retenu) vs SQLite (rejeté) : multi-utilisateur concurrent + serveur MariaDB déjà présent en PROD. Connexions en configuration (conteneurisées ou externes) sans changement de code.
- JWT HS256 (retenu) : simple à exploiter ; clé en configuration, démarrage refusé si absente. Passage à RS256 possible plus tard.
- Contrats de Modules (plugins applicatifs) (retenu) vs socle monolithique (rejeté) : permet d'empiler les Modules sans modifier le socle.
- Packs inclusifs par niveau (retenu) vs composition module par module (rejeté au MVP) : trois Packs ordonnés simples à comprendre et facturer ; rattachement Module->Pack paramétrable.
- Rétrogradation = conservation + consultation seule (retenu) vs suppression/archivage (rejeté) : aucune perte de donnée ; consultation seule déduite de la présence de données.
- Onboarding réseau par l'Administrateur (retenu) : un Réseau est créé, provisionné et paramétré (thème) avant toute inscription de mandataire ; détail à affiner après l'expérience IAD.
Deployment
- docker-compose : services
backend(ASP.NET Core, non exposé),frontend(nginx servant la SPA Angular, publié sur un port hôte), et optionnellement des servicesdbMariaDB (Base Socle et bases réseau) avec volumes, pour les environnements sans serveur externe. - Bases externes : en PROD, les services
dbsont omis ; les chaînes de connexion (socle + réseaux) pointent vers le serveur MariaDB existant de l'exploitant. - Provisioning d'un réseau : création de la Base Réseau (schéma + seed), enregistrement du Réseau dans la Base Socle (
db_connection_key,theme_settings), passageis_provisioned = 1. Au lancement : réseauxiadetdemo. - Migrations : schéma socle appliqué à la Base Socle ; schéma réseau appliqué à chaque Base Réseau, de façon idempotente.
- Secrets par environnement : clé JWT, chaînes de connexion (socle + réseaux) et paramètres SMTP via variables d'environnement (
.env), jamais dans l'image. Démarrage refusé si la clé JWT est absente. - Reverse-proxy HTTPS externe (Caddy) relaie vers le frontend ; le backend ne gère pas la terminaison TLS.
- Healthcheck :
GET /healthsur le backend.