ISNAD Scientific Platform

Documentation Center
V1 · Référence officielle

Architecture

ISNAD — Règles de dépendance entre couches (E11-T2)

4 min de lectureMis à jour le 29 octobre 2026
Sur cette page

Complément de repository-structure.md. Aucune règle ci-dessous n'est appliquée par un outil automatisé pour l'instant (pas d'ESLint boundary rule, pas de madge/dependency-cruiser configuré) — ce document est la spécification à partir de laquelle un tel outil pourra être configuré dans une micro-tâche E11 ultérieure.

Graphe autorisé#

                 ┌────────────┐
                 │    app     │  (Next.js App Router)
                 └─────┬──────┘
                       │
        ┌──────────────┼───────────────┐
        ▼               ▼                ▼
   ┌─────────┐    ┌──────────┐    ┌───────────┐
   │ features│    │  server  │    │  shared   │
   └────┬────┘    └────┬─────┘    └─────┬─────┘
        │              │                 │
        ▼              ▼                 ▼
   ┌─────────┐    ┌──────────┐    ┌───────────────┐
   │  domain │◄───┤ (types)  │    │  core / lib    │
   └────┬────┘    └──────────┘    └───────────────┘
        │
        ▼
┌────────────────┐
│ infrastructure  │ (implémente les ports définis dans domain/repositories)
└────────────────┘

Lecture du graphe : une flèche A → B signifie « A peut importer B ». infrastructure pointe vers domain parce qu'elle implémente les interfaces qui y sont définies (dépendance d'implémentation, pas d'import au sens runtime dans l'autre sens — domain n'importe jamais infrastructure).

Règles par couche#

app/#

  • Peut importer : features/*, domain/* (rarement, seulement pour des types), server/*, shared/*, core/*, lib/*, types/*.
  • Ne doit pas : contenir de la logique métier scientifique inline dans un page.tsx au-delà de l'assemblage UI — si une page fait plus que « charger des données + composer des composants », la logique doit vivre dans features/<nom>/services/ ou domain/.
  • Constat actuel : plusieurs pages (corpus/page.tsx, statistics/page.tsx, stats/page.tsx) appellent Supabase directement — toléré aujourd'hui (taille raisonnable), mais toute complexité supplémentaire doit être extraite vers server/ ou features/*/services/ plutôt que d'épaissir la page.

features/#

  • Peut importer : domain/*, server/* (temporairement, tant qu'un port domain/repositories/ n'existe pas pour cette feature — voir D2), shared/*, core/*, lib/*, types/*, et d'autres modules de la même feature.
  • Ne doit pas : importer directement une autre features/<autre-nom>/ — si deux features ont besoin du même composant/service, il migre vers shared/ ou core/, il ne reste pas dupliqué ni importé en croix.
  • Ne doit pas : accéder à infrastructure/ directement en contournant domain/repositories/ — si une feature a besoin de persistance formelle, elle passe par un port domain, jamais par un adapter infrastructure importé en direct.

domain/#

  • Peut importer : uniquement d'autres modules domain/* et types/* (types purs, sans dépendance).
  • Ne doit jamais importer : react, react-dom, tout module next/*, @supabase/supabase-js, pg, features/*, app/*, infrastructure/*, server/*, aucune API navigateur (fetch, localStorage, document, window).
  • Vérifié conforme aujourd'hui : grep exhaustif, 0 violation sur les 86 fichiers actuels de src/domain/.
  • Un domain/services/* peut recevoir une interface domain/repositories/* en paramètre (injection de dépendance) — cela reste autorisé car l'interface elle-même ne dépend de rien d'impur.

infrastructure/#

  • Peut importer : domain/repositories/* (pour implémenter leurs interfaces), core/*, lib/*, types/*, et des bibliothèques externes (pg, @supabase/supabase-js).
  • Ne doit jamais importer : react, next/*, app/*, features/*.
  • Une classe infrastructure/ implémente toujours un type exporté par domain/repositories/ (ex. SupabaseHadithAggregateWriter implements HadithAggregateWriter) — si un nouvel adapter n'implémente aucune interface domain, c'est probablement du server/, pas de l'infrastructure/ (voir D2).

server/#

  • Peut importer : domain/* (types/lecture uniquement, jamais pour invoquer une règle de décision complexe qui devrait être dans domain/services/), core/*, lib/*, types/*, pg, @supabase/supabase-js.
  • Ne doit jamais importer : react, JSX — un module server/ retourne des données typées, jamais des éléments React. Le rendu se fait dans app/ ou features/*/components/.
  • Ne doit pas être importé depuis un composant marqué 'use client' (règle déjà respectée aujourd'hui — c'est la raison d'être de ce dossier).

shared/#

  • Peut importer : core/*, lib/*, types/*.
  • Ne doit jamais : dépendre d'une features/<nom>/ spécifique, ni contenir de logique métier scientifique.
  • Un composant ne migre vers shared/ que lorsqu'il est déjà utilisé par au moins deux features distinctes — jamais par anticipation.

core/#

  • Peut importer : lib/*, types/*.
  • Ne doit jamais importer : features/* (le cœur ne connaît aucune feature spécifique — sinon ce n'est plus transverse).

lib/#

  • Peut importer : uniquement d'autres modules lib/* et types/*.
  • Ne doit jamais importer : domain/* (le sens est strictement inverse — domain/ peut en théorie utiliser une primitive lib/* très générique si nécessaire, mais lib/ ne remonte jamais vers domain/), ni features/*, ni React/Next.js pour les primitives pures (les quelques fichiers lib/*.tsx comme i18n.tsx/theme.tsx sont des candidats core/, pas des primitives — voir D7/D8).

Interdictions explicites (rappel condensé)#

domain        → features, app, React, Next.js, Supabase, pg, fetch     INTERDIT
shared        → une feature spécifique                                  INTERDIT
infrastructure→ app, features, React                                    INTERDIT
server        → React, JSX                                              INTERDIT
lib           → domain, features                                        INTERDIT
core          → features                                                INTERDIT
features/X    → features/Y (une autre feature)                          INTERDIT

Comment ces règles seront vérifiées (futur, non fait en E11-T2)#

Options envisageables pour une micro-tâche E11 ultérieure (non décidé ici, simple inventaire des options) :

  • dependency-cruiser ou madge avec un fichier de règles reproduisant le graphe ci-dessus, exécuté en CI.
  • Règle ESLint no-restricted-imports par dossier si l'équipe préfère rester dans l'outillage déjà présent (eslint n'est cependant pas configuré aujourd'hui dans ce repo — à vérifier lors de l'introduction de la CI, voir T1 risque « absence de CI »).

Aucun de ces outils n'est installé ou configuré par ce document.