Multi-tenancy
Vue d’ensemble
Section titled “Vue d’ensemble”L’application utilise stancl/tenancy pour isoler les données par cabinet/structure. Chaque Tenant possède sa propre base de données. Les données partagées (utilisateurs, subscriptions, networks) vivent en base centrale.
graph LR
subgraph Central DB
central_users
tenants
tenant_users["tenant_users (pivot)"]
tenant_invitations
subscriptions
networks
retrocession_accounts
end
subgraph Tenant DB["Tenant DB (1 par tenant)"]
users["users (sync depuis central)"]
patients
appointments
sites
billing_entities
end
central_users -. "ResourceSyncing" .-> users
Hiérarchie
Section titled “Hiérarchie”graph TD
Tenant["🏢 Tenant (cabinet/structure)"]
Tenant --> Site1["📍 Site 1 (lieu physique)"]
Tenant --> Site2["📍 Site 2"]
Tenant --> Sub["💳 Subscription (Stripe)"]
Site1 --> UserA["👤 User A (orthoptiste)"]
Site1 --> UserB["👤 User B (secrétaire)"]
Site1 --> Agenda["📅 Agenda, WaitingRoom..."]
Site2 --> UserC["👤 User C (ophtalmo)"]
Tenant
Section titled “Tenant”Le tenant est l’entité principale. Il représente un cabinet ou une structure médicale.
Modèle : src/Domain/Tenant/Models/Tenant.php
- Étend
Stancl\Tenancy\Database\Models\Tenant - Traits :
Billable(Cashier),TenantSubscription,HasDatabase - Porte toutes les infos de billing, quotas, features du plan
- Relation N-N avec
CentralUservia la pivottenant_users
Un site est un lieu physique au sein d’un tenant (cabinet, clinique).
Modèle : src/Domain/Site/Models/Site.php
- Appartient à un tenant (via la tenant DB)
- A des users (N-N avec pivot :
billing_entity_id,billing_entity_mode,proofreadings,expired_at) - A des agendas, salles d’attente, types de RDV
- Peut avoir une intégration Doctolib (
doctolib_client_id,doctolib_sync) - Configurable : thème documents (
DocumentLayoutTheme), mode rétrocession (SiteRetrocessionMode)
User (double modèle)
Section titled “User (double modèle)”| Modèle | Base | Rôle |
|---|---|---|
CentralUser | Centrale | Auth, profil global, tenants, networks, chat |
User | Tenant | Copie sync, relations locales (appointments, sites, proofreaders) |
Les attributs sont synchronisés automatiquement via Stancl\Tenancy\ResourceSyncing. La liste des champs synchronisés est définie dans getSyncedAttributeNames().
Auth : CentralUser implémente Authenticatable + HasApiTokens (Passport).
UserRole (rôle métier)
Section titled “UserRole (rôle métier)”| Valeur | Description |
|---|---|
user | Utilisateur plateforme (patient) |
prat_orthoptist | Orthoptiste |
prat_ophthalmologist | Ophtalmologue |
prat_secretary | Secrétaire |
prat_helper | Aide/assistant |
Les orthoptistes et ophtalmos doivent fournir un identifiant gouvernemental (prat_gouv_id).
Les secrétaires et helpers nécessitent un token d’invitation pour s’inscrire.
TenantRole (rôle sur le tenant)
Section titled “TenantRole (rôle sur le tenant)”| Valeur | Description |
|---|---|
manager | Administrateur du tenant (accès billing, invitations, settings) |
user | Membre standard |
Stocké dans la pivot tenant_users.tenant_role.
AdminRole
Section titled “AdminRole”UserAdminRole::ADMIN — super admin plateforme, accès au backoffice admin.
Headers de requête
Section titled “Headers de requête”Chaque requête API dans un contexte tenant utilise 2 headers :
| Header | Type | Description |
|---|---|---|
X-Tenant | uuid | ID du tenant — initialise la tenancy (switch de DB) |
X-Site | uuid | ID du site — contexte du lieu physique |
Le middleware de tenancy résout X-Tenant pour initialiser la connexion à la bonne base tenant.
Les routes protégées par subscribed vérifient que le tenant a un abonnement actif.
Invitations
Section titled “Invitations”Invitation tenant (TenantInvitation)
Section titled “Invitation tenant (TenantInvitation)”- Base centrale (
CentralConnection) - Contient :
email,user_role,tenant_role,sites(array de site IDs) - Token aléatoire de 32 chars
- Auto-purgée après 7 jours (
Prunable) - Flow :
send()→ notification email →accept(CentralUser)→ attach tenant + sites → delete invitation
Invitation relecteur (ProofreaderInvitation)
Section titled “Invitation relecteur (ProofreaderInvitation)”- Base tenant
- Relie un
requester_id(orthoptiste) à unproofreader_id(ophtalmo) - Flow :
send()→ notification →accept()(attach proofreaders) oudecline()(delete)
Fichiers clés
Section titled “Fichiers clés”| Fichier | Rôle |
|---|---|
src/Domain/Tenant/Models/Tenant.php | Modèle tenant avec billing et subscription |
src/Domain/Tenant/Models/TenantUsers.php | Pivot tenant ↔ user (tenant_role, expired_at) |
src/Domain/Tenant/Models/TenantInvitation.php | Invitation à rejoindre un tenant |
src/Domain/User/Models/CentralUser.php | Utilisateur en base centrale (auth) |
src/Domain/User/Models/User.php | Utilisateur en base tenant (sync) |
src/Domain/User/Enums/UserRole.php | Rôles métier |
src/Domain/Tenant/Enums/TenantRole.php | Rôles sur le tenant |
src/Domain/Site/Models/Site.php | Site (lieu physique) |