ISNAD Scientific Platform

Documentation Center
V1 · Référence officielle

ADR

ADR-0002 — Taxonomie des schémas de base de données

8 min de lectureMis à jour le 29 octobre 2026
Sur cette page
  • Statut : Accepté (2026-08-30 — validation mécanique du mapping : planning/audits/database-schema-taxonomy-validation.md, VERDICT PASS). GO migration conditionné à la Vague 0.
  • Date : 2026-08-30
  • Portée : toute la base PostgreSQL / Supabase, toutes les couches applicatives qui y accèdent
  • Références : planning/audits/b1-database-schema-audit.md, docs/architecture/domain-layer.md, docs/scientific/knowledge-model.md, planning/epics/b0-persistence-mapping-decision.md, ADR-0001, supabase/migrations/20260829150000_add_labs_hadithrank.sql

1. Contexte#

La base compte 61 tables métier (58 public + 3 labs), 28 enums, 6 vues, 65 fonctions et 43 triggers — tout dans public, sauf le schéma labs introduit le 2026-08-29 (inventaire vérifié : planning/audits/database-schema-taxonomy-validation.md §3). La séparation en couches décrite par docs/architecture/domain-layer.md s'arrête à l'application : côté base, aucune frontière de bounded context. Les préfixes de noms (narrator_*, commentary_*, transliteration_*) font le travail d'un schéma sans en donner les bénéfices (grants par domaine, SET SCHEMA, isolation d'exposition PostgREST, direction de dépendance visible).

L'audit planning/audits/b1-database-schema-audit.md conclut : restructuration faisable sans refonte applicative massive (client Supabase non typé → pas de régénération de types ; RPC conservables comme façade ; coût réel concentré sur ~54 appels supabase.from() directs et ~52 corps de fonctions référençant public.*). Le moment est optimal : pré-lancement, un seul précédent (labs) déjà conforme.


2. Décision#

2.1 Dix schémas par bounded context#

public cesse d'héberger des tables métier. Il ne conserve que la façade API (fonctions RPC exposées, vues-façade stables) et les utilitaires transverses (triggers génériques, normalisation). Le métier est réparti dans dix schémas :

CodeDomaineCouche domain-layer.mdDépend deExposé PostgREST
corCorpus canonique (hadith, isnād, embeddings)Domain—oui (lecture)
narNarrators / rijāl (prosopographie)Domaincoroui (lecture)
knwKnowledge — acquisition savante + assertions/evidenceDomainnar, coroui (lecture)
recReconciliation — entity resolution, crosswalk, feedbackEnginenar, knwpartiel
comCommentary — sharḥ / Fatḥ al-BārīDomaincoroui (lecture)
trlTransliteration — moteur RUWAT FREnginenar (lecture)partiel
ingIngestion — provenance, batches, runsInfrastructureécrit partoutnon
quaQuality Engine — snapshots, décisions, métriquesEnginelit partoutnon
iamComptes applicatifsInfrastructure—non
labLabs — modèles exploratoires (renommage de labs)hors architecturerien ne doit en dépendrevia ponts public.*

Direction de dépendance (flèche pleine = dépendance structurelle déclarée ; flèche pointillée = lecture transverse par un schéma de plomberie, non exposé) :

2.2 Convention de nommage (figée)#

  1. Code de 3 lettres, anglais, [a-z]{3} strict. Pas de _, pas de chiffre, pas de préfixe projet. iam toléré (acronyme établi).
  2. Racines arabes écartées (rij, rwt, srh) — préférence explicite pour l'anglais.
  3. Un schéma = un bounded context, jamais une couche technique abstraite (data, app).
  4. La direction de dépendance est déclarée dans comment on schema et ne doit jamais s'inverser (cor ne référence jamais nar, etc.).
  5. Toute évolution de cette liste = amendement de cette ADR + b1-database-schema-audit.md.

2.3 Règles d'appartenance#

  • cor porte l'agrégat hadith entier : hadith_chains, hadith_chain_nodes, hadith_embeddings y restent. Pas de schéma rel — la chaîne est l'isnād d'un hadith, l'écriture est atomique, l'agrégat = la frontière. Les futures tables calculées du graphe (poids d'arêtes, caches de chemins, réseaux agrégés) iront dans qua ou un schéma dédié, jamais les tables sources.
  • knw n'est pas scindé : knowledge_sources → source_editions → knowledge_entries → narrator_assertions → assertion_evidence forment une seule chaîne d'un seul contexte (docs/scientific/knowledge-model.md). Pas de src / evd.
  • public = façade uniquement : RPC exposées + vues-façade stables + helpers transverses. Zéro table métier.
  • Les tables projetées vers l'utilisateur mais alimentées par un moteur (ex. narrator_quality_profiles) vivent dans le schéma lu (nar), pas dans le schéma écrivain (qua). Le sens d'écriture est documenté en commentaire de table.

2.4 Accès applicatif#

  • La UI n'appelle plus supabase.from() directement. Les lectures passent par des adapters src/infrastructure/supabase/<Domain>RepositorySupabase.ts qui portent le .schema() (DOMAIN_LAYER_V1.md §4).
  • Les RPC restent dans public et constituent la couche API officielle ; seuls leurs corps sont re-qualifiés vers les schémas métier.
  • Les vues-façade public.* sont réservées à de vraies API stables partagées, pas au masquage d'un couplage applicatif.
  • Fonctions SECURITY DEFINER : search_path minimal + qualification explicite totale des références dans le corps (from nar.narrators join cor.hadith_chain_nodes). Aucune résolution implicite.

3. Mapping exhaustif (D4)#

3.1 Tables#

SchémaTables
corcollections, books, chapters, hadiths, hadith_chains, hadith_chain_nodes, hadith_embeddings
narnarrators, narrator_name_forms, narrator_appellations, narrator_nisbas, narrator_kinships, narrator_locations, narrator_collections, narrator_transmissions, narrator_reliability, narrator_identities, narrator_identity_variants, narrator_quality_profiles (écrite par qua), external_narrators
knwknowledge_sources, source_editions, knowledge_entries, narrator_assertions, assertion_evidence
recnarrator_crosswalk, narrator_crosswalk_candidates, narrator_data_conflicts, narrator_reconciliation_decisions, reconciliation_decision_assertions, narrator_resolution_feedback, resolution_feedback_audit
comcommentary_sources, commentary_editions, commentary_documents, commentary_passages, commentary_passage_labels, commentary_targets, commentary_target_hadiths, commentary_alignments, commentary_alignment_evidence, commentary_translations, commentary_validated_rules, commentary_fath_manifest
trltransliteration_standards, transliteration_lexicon, transliteration_aliases, transliteration_stopwords, transliteration_dry_run_runs, transliteration_narrator_proposals
ingdata_sources, datasets, import_batches, processing_runs
quadata_quality_snapshots, quality_engine_state, quality_decisions
iamadmins
labhadithrank_grade_fidelity, hadithrank_narrator_override, hadithrank_run

3.2 Vues#

CibleVueNote
narnarrator_canonical_displayprojection d'affichage
comcommentary_current_validated_rules, commentary_hadith_passages
corhadith_index_rows
publicpublic_narrator_assertions, public_transliteration_lexiconvues-façade : à conserver comme API stable (lecture knw / trl filtrée) ou à convertir en RPC lors de la Vague 4

3.3 Enums#

SchémaEnums
knwknowledge_source_work_type, knowledge_entry_resolution_status, narrator_assertion_field_type, assertion_basis, assertion_canonical_status, assertion_evidence_type, assertion_relation_type
recreconciliation_decision_type, reconciliation_assertion_role, data_conflict_status
quaquality_decision_kind, narrator_quality_status
ingprocessing_run_type, processing_run_status
comcommentary_work_type, commentary_edition_role, commentary_manifestation_kind, commentary_offset_unit, commentary_passage_kind, commentary_provenance_status, commentary_rights_status, commentary_target_type, commentary_translation_status, commentary_translator_type, commentary_verification_status, commentary_alignment_status, commentary_alignment_evidence_type, commentary_rule_validated_status

3.4 Fonctions#

DestinationFonctions
public (façade API, corps re-qualifié)browse_hadith_index, browse_narrator_dictionary, search_narrators, search_narrators_translit, search_narrator_candidates, search_narrator_candidates_translit, search_hadiths_arabic, find_chain_edges, get_narrator_hadith_occurrences, dashboard_totals, dashboard_grade_distribution, dashboard_region_distribution, dashboard_generation_distribution, dashboard_collection_distribution, dashboard_top_connected, get_public_quality_statistics, get_public_transliteration_stats, get_admin_data_quality, get_admin_transliteration_quality, get_admin_transliteration_impact_queue, is_admin, review_resolution_feedback, review_narrator_assertion, apply_crosswalk_decision, set_transliteration_lexicon_review_status, batch_approve_transliteration_lexicon, start_transliteration_dry_run, record_transliteration_dry_run, append_transliteration_dry_run_proposals, labs_hadithrank_hadith_chains, labs_hadithrank_grade_fidelity, labs_hadithrank_narrator_overrides
public (helpers transverses)set_updated_at, deny_update_delete, immutable_unaccent, arabic_initial, normalize_ar_matn, normalize_ar_query, normalize_translit, extract_hadith_index_matn
quacompute_data_quality_metrics, create_data_quality_snapshot, compute_night_shift_provenance_metrics, validate_quality_run
trlcompute_transliteration_quality_metrics
ingprotect_terminal_processing_run
knw (triggers de validation)validate_assertion_supersession, validate_narrator_assertion_value, protect_narrator_assertion_observation, validate_name_structure
rec (triggers de validation)protect_crosswalk_projection, validate_data_conflict, validate_decision_assertion, validate_decision_cardinality, validate_decision_supersession, validate_effective_canonical_cardinality
com (triggers de validation)validate_commentary_alignment_supersession, validate_commentary_document_supersession, validate_commentary_multiple_target_cardinality, validate_commentary_passage_group, validate_commentary_passage_supersession, validate_commentary_target_hadith_type, validate_commentary_translation_supersession, validate_commentary_validated_rule_evidence, validate_commentary_validated_rule_supersession

Un trigger de validation vit dans le schéma de la table qu'il protège. Les helpers réellement génériques restent dans public.


4. Séquencement#

VagueContenuEffort
0Snapshot complet + test de restauration ; geler l'inventaire §3 (diff avec la base réelle, PGlite jetable) ; geler la liste dans cette ADR. Aucun alter.faible
1ing, qua, iam ; renommer labs → lab. Feuilles techniques, ~0 accès app direct. Valide le playbook (create schema → set schema → re-qualifier → grants → golden test).faible
2cor, nar. + repositories src/infrastructure/supabase/ (corpus, narrators) remplaçant les .from() directs.élevé (SQL) / moyen (TS)
3knw, com.moyen
4rec, trl ; conversion des vues public_* ; réduction finale de public à la façade ; suppression des compat devenues inutiles.moyen

Prérequis avant Vague 1 : cette ADR passée en Accepté, §3 validé contre la base réelle, Vague 0 exécutée. Cible : avant S1.5, sans bloquer L1.4-A.


5. Playbook de migration (par schéma)#

begin;

create schema <ctx>;
comment on schema <ctx> is '<domaine> — dépend de <…>. Aucune dépendance entrante depuis <…>.';
grant usage on schema <ctx> to anon, authenticated;

alter table public.<t> set schema <ctx>;          -- opération catalogue, pas de réécriture
alter type  public.<e> set schema <ctx>;
alter view  public.<v> set schema <ctx>;

-- fonctions : recréer avec corps qualifié + search_path explicite
create or replace function <ctx>.<f>(...) ... 
  set search_path = <ctx>, cor, nar, public, pg_temp
  as $$ ... from <ctx>.<t> ... $$;

grant select on all tables in schema <ctx> to anon, authenticated;   -- si exposé
alter default privileges in schema <ctx> grant select on tables to anon, authenticated;

commit;

Puis, hors migration : ajouter <ctx> aux Exposed schemas du projet Supabase si lecture directe voulue ; sinon la façade RPC public suffit. Golden test SQL dédié (supabase/tests/).


6. Conséquences#

Positives

  • Frontières de bounded context matérialisées et non contournables ; direction de dépendance lisible.
  • Surface PostgREST réduite de 58 tables public à une façade RPC choisie + quelques vues.
  • Grants par domaine (grant … on all tables in schema) au lieu de table par table.
  • ing et qua (plomberie) deviennent non exposés → moindre privilège.
  • La migration force enfin la séparation Domain / Infrastructure côté app (~54 .from() → ~5 repositories).

Négatives / coûts

  • ~52 fonctions à recréer avec corps qualifié (SQL, une fois, couvert par golden tests).
  • ~54 sites d'appel .from() à router via repositories (Vagues 2–3).
  • Chaque nouvelle migration doit qualifier cor. / nar. / … ; les fixtures de test créent les schémas.
  • search_path des fonctions SECURITY DEFINER à auditer un par un.

Neutres

  • FK inter-schémas : supportées nativement par PostgreSQL, aucun changement.
  • RLS : alter table set schema conserve les policies ; auth.uid() inchangé.
  • Types TS : client non typé, rien à régénérer.

7. Alternatives écartées#

AlternativeRaison du rejet
Statu quo (tout public)Dette croissante ; surface API = toutes les tables ; grants ingérables.
Schémas par couche technique (domain, engine, infra)Ne dit rien du quoi ; un schéma domain à 35 tables reproduit le problème.
Schéma rel séparé pour chaînes/nœudsFrontière artificielle ; casse l'atomicité de l'agrégat hadith ; le Reasoning Engine lit à travers les schémas de toute façon.
knw scindé en src / evdSur-fragmentation d'une chaîne à contexte unique.
Tout re-exposer via vues public.*Masque indéfiniment le couplage applicatif que la migration doit résorber.
Codes longs (corpus, narrators)Verbeux en SQL qualifié ; l'équipe a tranché pour 3 lettres.

8. Suivi#

  • §3 diffé contre la base réelle — planning/audits/database-schema-taxonomy-validation.md (2026-08-30, PASS : 61/61 tables, 6/6 vues, 28/28 enums, 65/65 fonctions, 43/43 triggers, 0 duplicate, 0 contradiction D1–D3)
  • ADR passée en Accepté (2026-08-30)
  • Vague 0 : snapshot + restore test + manifeste d'ordre du socle supabase/*.sql (blocage B1 du rapport de validation)
  • Vague 1 exécutée + golden test
  • planning/audits/b1-database-schema-audit.md §7 D4 coché