Aller au contenu

PD-236 — Plan d'implémentation

1. Découpage en composants

Composant Responsabilité Dépendances
TokenSearchController Exposition API (indexation, recherche) TokenSearchService, RequestValidator, ProtocolMapper
RequestValidator Validation des requêtes (T_kw format, métadonnées) TokenFormatValidatorRegistry
TokenFormatValidatorRegistry Registre de validateurs de format T_kw par algorithm_id TokenFormatValidator[]
TokenFormatValidator Interface de validation du format T_kw pour un algorithm_id donné
TokenSearchService Orchestration métier (indexation, recherche, vérification M_I) TokenIndexRepository, MetadataRepository
TokenIndexRepository Persistance des associations (T_kw, resource_id) avec égalité stricte PostgreSQL
MetadataRepository Persistance et lecture de M_I par index_id PostgreSQL
AuditEventEmitter Émission d'événements d'audit (ERR-BE-05) EventBus/Logger
ProtocolMapper Abstraction du mapping ERR-BE-xx vers le protocole de transport

2. Flux techniques

2.1 Indexation

Client → TokenSearchController
    → RequestValidator.validateIndexationRequest(body)
        → TokenFormatValidatorRegistry.getValidator(algorithm_id).validate(T_kw)
        → vérifier présence métadonnées {algorithm_id, canonicalization_id, k_search_scope}
        → rejeter si champ "keyword" en clair détecté → AuditEventEmitter.emit()
    → TokenSearchService.index(index_id, entries, metadata)
        → MetadataRepository.getOrInitialize(index_id, M_req)
            → si M_I existe : comparer M_req == M_I (égalité champ-à-champ)
            → si M_I n'existe pas : persister M_I = M_req (avec garantie d'atomicité)
            → si mismatch → ERR-BE-04
        → pour chaque (T_kw, resource_id) : TokenIndexRepository.persist(index_id, T_kw, resource_id)
    → Succès | Rejet avec ERR-BE-xx

2.2 Recherche

Client → TokenSearchController
    → RequestValidator.validateSearchRequest(body)
        → TokenFormatValidatorRegistry.getValidator(algorithm_id).validate(T_kw)
        → vérifier présence métadonnées
    → TokenSearchService.search(index_id, T_kw, metadata)
        → MetadataRepository.get(index_id)
            → si M_I n'existe pas : retourner ensemble vide (index non initialisé)
            → comparer M_req == M_I : si mismatch → ERR-BE-04
        → TokenIndexRepository.findByToken(index_id, T_kw) [égalité stricte]
    → Succès { resource_ids: [...] } | Rejet avec ERR-BE-xx

Note : Le comportement « index non initialisé » (M_I inexistant lors d'une recherche) n'est pas explicitement défini par la spec. Le choix retenu est de retourner un ensemble vide de resource_id, ce qui est sémantiquement cohérent (aucune association n'existe). Ce point est consigné comme hypothèse technique (HT-06).


3. Mapping invariants → mécanismes

Invariant ID Exigence Mécanisme Composant Observable Risque
INV-01 Pas de keyword en clair reçu ni persisté Schéma API sans champ "keyword" + détection champ interdit + aucun stockage clair RequestValidator, TokenIndexRepository Inspection payload API + dump DB Champ non prévu dans body JSON
INV-02 Recherche par égalité stricte uniquement Requête SQL WHERE token = $1 (opérateur =, pas de LIKE/fuzzy/full-text) + configuration stockage exact match TokenIndexRepository Query plan / logs / tests Configuration DB altérée
INV-03 Métadonnées homogènes par index M_I immuable après création, comparaison stricte à chaque requête MetadataRepository, TokenSearchService Contrainte DB unique (index_id) + assertions Race condition à la création
INV-04 Mismatch → rejet explicite Comparaison champ-à-champ M_req vs M_I, retour ERR-BE-04 si différence TokenSearchService Code erreur dans réponse Comparaison partielle oubliée
INV-05 Pas d'inférence sémantique Aucun traitement NLP/ML sur T_kw, traitement opaque Architecture (absence de code) Revue de code Non testable en boîte noire

4. Mapping critères d'acceptation → mécanismes

Critère ID Mécanisme(s) Composant Observable Risque
CA-01 Validation format T_kw via validateur spécifique à l'algorithm_id TokenFormatValidatorRegistry Rejet avec ERR-BE-02 si invalide Validateur manquant pour un algorithm_id
CA-02 Query exacte SELECT resource_id FROM entries WHERE index_id = $1 AND token = $2 TokenIndexRepository Résultat = ensemble attendu Index DB manquant ou mal configuré
CA-03 Schéma DB sans colonne "keyword", schéma API sans champ "keyword" Schema migration, Contrat API Inspection schema Migration non appliquée
CA-04 Comparaison M_req.algorithm_id == M_I.algorithm_id && M_req.canonicalization_id == M_I.canonicalization_id && M_req.k_search_scope == M_I.k_search_scope TokenSearchService ERR-BE-04 retourné Oubli d'un champ
CA-05 Opérations INSERT atomiques, pas d'UPDATE des entrées existantes TokenIndexRepository Requêtes avant/après identiques pour tokens existants Transaction mal configurée

5. Mapping tests (TC-*) → mécanismes + observables

Test ID Référence spec Mécanisme(s) Point(s) d'observation Niveau de test
TC-NOM-01 §5.0, §5.1, CA-01, INV-03 Validation format + persistance + vérification M_I Réponse succès, DB contient N entrées, M_I cohérent Integration
TC-NOM-02 §5.2, CA-02, INV-02 Query exacte TokenIndexRepository Réponse succès, resource_ids = expected set Integration
TC-NOM-03 CA-03, INV-01 Absence champ clair dans schema Dump DB, payload API Integration + Inspection
TC-NOM-04 §5.0, §5.2, INV-03 Comparaison M_req vs M_I Q1 → succès, Q2 → rejet ERR-BE-04 Integration
TC-ERR-01 ERR-BE-01 RequestValidator.validatePresence(T_kw) Réponse rejet, code ERR-BE-01 Unit + Integration
TC-ERR-02 ERR-BE-02 TokenFormatValidatorRegistry.validate() Réponse rejet, code ERR-BE-02 Unit + Integration
TC-ERR-03 ERR-BE-03 RequestValidator.validateMetadata() Réponse rejet, code ERR-BE-03 Unit + Integration
TC-ERR-04 ERR-BE-04, INV-04 TokenSearchService.checkMetadataMatch() Réponse rejet, code ERR-BE-04, raison mismatch Integration
TC-ERR-05 ERR-BE-05, INV-01 RequestValidator.detectClearKeyword() + AuditEventEmitter Réponse rejet, event audit émis Integration + Audit log
TC-INV-01 INV-01 Schema validation + inspection Aucun clair en DB/API Integration + Inspection
TC-INV-02 INV-02 Query exacte uniquement Résultat T1 = expected, T2 = vide Integration
TC-INV-03 INV-03 M_I immuable Rejet si M_L != M_I Integration
TC-INV-04 INV-04 checkMetadataMatch() Rejet explicite Integration
TC-INV-05 INV-05 NON TESTABLE
TC-NR-01 CA-05 INSERT only, pas d'UPDATE Résultats S0 = S1 pour tokens existants Integration
TC-NR-02 CA-05 Isolation des tokens Résultats inchangés après ajout nouveau token Integration
TC-NEG-01 ERR-BE-03 validateMetadata() Rejet Integration
TC-NEG-02 ERR-BE-04 checkMetadataMatch(k_search_scope) Rejet mismatch scope Integration
TC-NEG-03 ERR-BE-05 detectClearKeyword() Rejet + audit Integration

6. Gestion des erreurs

Code Situation Réponse Observable
ERR-BE-01 T_kw absent Rejet explicite avec code ERR-BE-01 et raison contractuelle Log applicatif
ERR-BE-02 T_kw format invalide pour l'algorithm_id déclaré Rejet explicite avec code ERR-BE-02 et raison contractuelle Log applicatif
ERR-BE-03 Métadonnées absentes/incomplètes Rejet explicite avec code ERR-BE-03 et raison contractuelle Log applicatif
ERR-BE-04 Mismatch métadonnées (M_req != M_I) Rejet explicite avec code ERR-BE-04 et raison contractuelle Log applicatif
ERR-BE-05 Keyword en clair détecté Rejet explicite avec code ERR-BE-05 et raison contractuelle Event audit + log sécurité

Abstraction protocolaire : Le mapping des codes ERR-BE-xx vers un protocole de transport (HTTP, gRPC, etc.) est délégué au composant ProtocolMapper. Ce mapping est un point à clarifier selon la spec (§10). L'implémentation doit permettre de configurer ce mapping sans modifier la logique métier.

Garanties transactionnelles : Tout rejet n'entraîne aucune modification d'état (rollback implicite ou absence de commit).


7. Impacts sécurité

Risque Mitigation Observable
Injection de keyword en clair Détection de champ "keyword" dans le body JSON, rejet ERR-BE-05 Event audit
Fuite de T_kw dans les logs Masquage/troncature des T_kw dans les logs applicatifs Configuration logger
Attaque par fréquence/dictionnaire Hors périmètre (risque accepté, cf. §2.2 spec)
Accès non autorisé à l'index Authentification/autorisation en amont (hors scope PD-236)
Corruption de M_I Contrainte UNIQUE sur (index_id) dans table metadata + atomicité à la création Contrainte DB

Journalisation obligatoire : - ERR-BE-05 → event audit avec timestamp, requête anonymisée, IP source - Toutes les opérations d'indexation/recherche loguées (sans T_kw en clair)


8. Hypothèses techniques

ID Hypothèse Justification Impact si faux
HT-01 Le format T_kw est défini par PD-42 pour chaque algorithm_id (ex: DET-HMAC-SHA256-B64T22-V1 → Base64 URL-safe sans padding, longueur 22) Référence PD-42 §4.3 Validation incorrecte (faux positifs/négatifs)
HT-02 Le registre de validateurs (TokenFormatValidatorRegistry) est extensible pour supporter plusieurs algorithm_id La spec ne restreint pas à un seul algorithm_id Refus de tokens valides si algorithm_id inconnu
HT-03 L'index_id est fourni par le client dans la requête Choix d'implémentation (non spécifié par la spec) Changement de contrat API si génération serveur
HT-04 La création de M_I est atomique : si deux requêtes concurrentes tentent de créer M_I pour le même index_id, une seule réussit et l'autre compare avec M_I créé Garantie INV-03 Race condition → incohérence M_I
HT-05 PostgreSQL est utilisé pour la persistance avec configuration par défaut (collation, opérateur = exact) Choix d'implémentation Adaptation si autre SGBD
HT-06 Une recherche sur un index non initialisé (M_I inexistant) retourne un ensemble vide de resource_id Comportement non spécifié par la spec, choix cohérent sémantiquement Divergence si autre interprétation attendue
HT-07 Le stockage garantit l'égalité stricte (exact match) pour les requêtes sur T_kw : pas de collation case-insensitive, pas d'index full-text Garantie INV-02 Résultats incorrects si recherche floue activée

9. Points de vigilance (risques, dette, pièges)

Point Description Recommandation
Race condition M_I Deux requêtes concurrentes sur un nouvel index peuvent créer des M_I différents Utiliser INSERT ... ON CONFLICT DO NOTHING + relecture immédiate avec verrou
Validateurs par algorithm_id Chaque algorithm_id de PD-42 requiert un validateur dédié Implémenter un registre extensible (pattern Strategy)
Détection keyword clair La spec mentionne "champ explicitement qualifié de keyword" → définir la liste des champs interdits dans le contrat API Documenter dans le contrat API
Performance recherche Index DB sur (index_id, token) obligatoire pour garantir performance Migration à valider
Collation DB La collation par défaut de PostgreSQL doit garantir l'égalité stricte Vérifier collation = 'C' ou binaire sur la colonne token
Comparaison M_I stricte Attention à l'ordre des champs JSON si sérialisation → utiliser comparaison champ-à-champ Tests sur ordre de champs différent
Mapping protocolaire Le mapping ERR-BE-xx vers HTTP/gRPC est un point à clarifier (§10 spec) Implémenter via abstraction ProtocolMapper

10. Hors périmètre

  • Génération/canonicalisation/tokenisation des keywords (PD-42)
  • Recherche floue, partielle, full-text, sémantique
  • Scoring/ranking des résultats
  • Protection contre analyse de fréquence/dictionnaire (risque accepté)
  • Migration d'index existants (évolution PD-42)
  • Purge/reconstruction d'index
  • Authentification/autorisation (géré en amont)
  • Choix du protocole de transport (HTTP/gRPC) — abstrait via ProtocolMapper

Références

  • Spec : PD-236-specification.md
  • Tests : PD-236-tests.md
  • Dépendance : PD-42 — Recherche déterministe chiffrée
  • Epic : PD-186 BACKEND CORE