Base de données multi‑tenant (dynors-db)
Objectif : expliquer comment un projet client utilise dynors-db pour isoler les données des clients (tenants) sans réinventer la roue.
1. Rappels : tenant, tiers, stratégie
Dans dynors-db :
- Un tenant représente un client logique (SeckShop, MIAMCAMPUS Sénégal, JOKKO Interne, …).
- Un tier détermine le niveau de service (FREE, TIER_3, TIER_2, TIER_1, SOVEREIGN).
- Une stratégie d’isolation détermine comment les données sont séparées :
SHARED: une base, un schéma, une colonnetenant_id.SCHEMA_DEDICATED: un schéma par tenant.DATABASE_DEDICATED: une base par tenant.
Pour les projets clients ESN (CLIENT_PROJECT) :
- Règle actuelle : SCHEMA_DEDICATED obligatoire.
- TAKKU choisit le tier et crée le schéma (via TenantInitializationService).
👉 L’app client ne gère pas la création des tenants ni des schémas : elle consomme ce que TAKKU a préparé.
2. Dépendances & configuration
Dans build.gradle.kts :
dependencies {
implementation(platform("com.dynors:dynors-core-bom:1.0.0"))
implementation("com.dynors:dynors-db")
}
Dans application.yml :
dynors:
db:
multi-tenant: true
default-strategy: schema # SCHEMA_DEDICATED pour les projets clients
default-tier: TIER_2
spring:
datasource:
url: jdbc:postgresql://localhost:5432/dynors_db
username: ${DB_USER}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
3. Entités & schémas côté projet client
En stratégie SCHEMA_DEDICATED :
- Les tables métier (ex :
customers,orders, …) sont créées dans le schéma du tenant (ex :tenant_seckshop). - L’application ne rajoute pas de colonne
tenant_iddans chaque table : - l’isolation est assurée par le schéma,
- le code reste "classique" côté JPA.
Exemple :
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, unique = true)
private String email;
}
Les scripts Flyway créent simplement customers dans le schéma courant, sans gérer le schéma lui‑même.
4. Repositories tenant‑aware
Pour bénéficier de l’isolation automatique, les repositories métier doivent étendre TenantAwareRepository :
@Repository
public interface CustomerRepository extends TenantAwareRepository<Customer, UUID> {
Optional<Customer> findByEmail(String email);
}
TenantAwareRepository se charge de :
- appliquer le bon schéma / le bon filtre en fonction du tenant courant,
- éviter les fuites de données entre tenants.
5. TenantContext & services dynors-db
dynors-db fournit plusieurs services utiles dans un projet client :
TenantContextService:getCurrentTenant()→ code du tenant courant.- utilisé pour enrichir les logs, appliquer certaines règles métier, etc.
QuotaService:- contrôle des quotas (stockage, nombre d’utilisateurs, appels API…).
Exemple :
@Autowired
private TenantContextService tenantContextService;
@Autowired
private QuotaService quotaService;
public void createCustomer(Customer customer) {
String tenant = tenantContextService.getCurrentTenant();
// Vérifier les quotas avant l’opération
quotaService.checkUsersQuota(tenant);
// ... créer le client ...
}
👉 C’est TenantContextFilter (fourni par dynors-db) qui positionne automatiquement le tenant courant pour chaque requête HTTP.
6. Migrations Flyway
- Les migrations sont placées dans
src/main/resources/db/migration. - Le schéma tenant est géré par
dynors-db/ TAKKU, pas par les scripts. - Chaque script crée/modifie uniquement les tables métier.
Exemple V1__create_customers_table.sql :
CREATE TABLE IF NOT EXISTS customers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
7. Points d’attention
- Ne pas contourner
TenantContextpar des requêtes SQL brutes sans filtre. - Ne pas créer les schémas/database à la main dans les migrations.
- Toujours vérifier que les tests d’intégration tournent avec un tenant défini (sinon les repositories tenant‑aware peuvent échouer).
Pour les détails avancés (tiers, stratégies, quotas complets), se référer à :
- core/packages/core/db/README.md
- core/packages/core/db/ARCHITECTURE_FINALE.md
- core/CODES_ET_IDENTIFIANTS_PROJETS.md.