Files
khadhroony-solana-project/docs/architecture/005-DEPENDENCY_GRAPH.md

22 KiB

Graphe de dépendances KSP

Objet

Ce document constitue la sortie principale de 0.0.3-pre.003.

Il répond à la question : quels composants peuvent dépendre de quels autres composants, et où doivent se produire la composition, l'I/O et les conversions de modèles ?

Convention des graphes :

A -> B

signifie : A peut dépendre de B.

Le graphe exprime une direction architecturale. Une dépendance autorisée n'est pas obligatoire si l'implémentation peut rester plus faible.

Principes structurants

1. APIs sous les implémentations

Une crate ksp-<domain>-api est placée sous les implémentations de son domaine.

implementation -> domain-api

Une API publique ne dépend pas de son implémentation officielle.

2. Transformations séparées de l'I/O

Les bibliothèques de sémantique/transformation comme ksp-program-lib et ksp-materializer-lib ne deviennent pas propriétaires :

  • du transport réseau ;
  • du wallet ;
  • du backend PostgreSQL ;
  • du lifecycle d'un worker/job.

Les workers, jobs, scenarios et pipelines spécialisés composent ces capacités lorsqu'ils ont réellement besoin d'I/O.

3. Modèles de domaine distincts des DTO persistants

KSP ne crée pas actuellement un ksp-data-api global.

Chaque frontière possède les modèles nécessaires à sa responsabilité :

  • transport : modèles homogènes de transport ;
  • program : contrats de décodage/opération préparée ;
  • materializer : entrées/sorties de matérialisation ;
  • store : modèles persistants et références de données persistées.

La conversion entre ces modèles est explicite dans la couche qui relie les deux responsabilités, principalement les workers/jobs spécialisés.

4. Composition supérieure pour l'exécution

ksp-program-lib ne dépend pas de ksp-wallet-lib ou de ksp-onchain-transport-lib.

Le cycle signature/simulation/envoi/confirmation est composé au-dessus, par ksp-execution-lib.

5. Workers et jobs restent deux familles

ksp-worker-api et ksp-job-api n'ont pas de parent lifecycle commun.

Un orchestrateur futur peut consommer les deux familles séparément sans les fusionner.


Graphe fondations N1

ksp-core-lib

ksp-logging-lib   -> ksp-core-lib
ksp-config-lib    -> ksp-core-lib
ksp-config-lib    -> ksp-logging-lib        # lorsque du runtime logging est nécessaire
ksp-interface-lib -> ksp-core-lib
ksp-interface-lib -> ksp-logging-lib        # lorsque du runtime logging est nécessaire

ksp-core-lib ne dépend d'aucun composant KSP supérieur.

ksp-interface-lib est propriétaire de la façade wire et peut dépendre des crates externes Solana/interface explicitement autorisées par RULES_DEPENDENCIES.md.

ksp-logging-lib est la façade logging/tracing KSP et peut dépendre de ksp-core-lib pour Error / Result. ksp-core-lib n'a pas de dépendance inverse vers le logging. Les composants comportant du runtime peuvent dépendre directement de ksp-logging-lib; cette permission n'oblige pas les crates purement déclaratives à le faire.


Graphe Logging

ksp-logging-lib
    -> ksp-core-lib
    -> tracing / tracing-appender / tracing-subscriber

runtime crate
    -> ksp-logging-lib

ksp-logging-lib est la seule façade KSP propriétaire de l'initialisation/configuration tracing. Une crate runtime KSP n'utilise pas directement tracing, tracing-subscriber ou tracing-appender pour émettre ses propres événements/spans.

Le subscriber global KSP est installé une seule fois puis sa configuration peut être rechargée à chaud par ksp-logging-lib. Les targets externes sont silencieux par défaut ; les informations tierces utiles sont réémises par le composant KSP propriétaire sous son propre target.

Exception technique possible : une application/framework, notamment Tauri, peut devoir intégrer un plugin tracing. Cette adaptation ne crée pas une seconde politique de logging parallèle et les événements KSP restent émis via la façade KSP.

ksp-core-lib et les crates *-api purement déclaratives n'ont pas de dépendance logging obligatoire.


Propriété des codecs wire

Pour les surfaces wire officielles KSP :

ksp-interface-lib
    -> codecs wire nécessaires (borsh/wincode/...)

ksp-program-lib n'ajoute pas directement une autre génération de codec pour les mêmes surfaces.

Une extension Program externe peut temporairement posséder son wire lorsqu'il n'est pas encore officiellement intégré à ksp-interface-lib.

Le critère d'adoption d'une crate d'interface externe inclut la compatibilité de son graphe de dépendances avec le stack KSP actuel.


Graphe Program

ksp-program-api
    -> ksp-core-lib
    -> ksp-interface-lib

ksp-program-lib
    -> ksp-program-api
    -> ksp-interface-lib
    -> ksp-core-lib

Frontière de ksp-program-api

ksp-program-api doit porter les contrats publics nécessaires à une implémentation externe de decoder/execution preparation.

Il est également le propriétaire candidat du contrat d'opération préparée produit par la sémantique programme et consommé par la couche d'exécution.

Le contrat de préparation retenu est centré sur :

ProgramExecutionRequest
        ↓
ProgramExecutionPreparer
        ↓
PreparedProgramExecution

Les champs Rust exacts restent à définir avec la première implémentation, sans rouvrir la séparation préparation/exécution.

Interdictions Program

ksp-program-api -X-> ksp-wallet-lib
ksp-program-api -X-> ksp-onchain-transport-lib
ksp-program-api -X-> ksp-store-api
ksp-program-api -X-> ksp-materializer-api

ksp-program-lib -X-> ksp-wallet-lib
ksp-program-lib -X-> ksp-onchain-transport-lib
ksp-program-lib -X-> ksp-store-api
ksp-program-lib -X-> ksp-store-lib
ksp-program-lib -X-> ksp-materializer-lib
ksp-program-lib -X-> ksp-execution-lib

Le decoder/execution preparation de programme reste donc testable sans réseau, wallet ou base de données.


Implémentations Program externes

Une implémentation externe peut dépendre de :

ksp-program-<name>-lib
    -> ksp-program-api
    -> ksp-core-lib
    -> ksp-interface-lib        # lorsque le wire officiel nécessaire existe

Elle ne dépend pas obligatoirement de ksp-program-lib.

Le runtime peut composer registry officiel + registry/implémentations externes derrière ksp-program-api.

Graphe Execution / Policy

ksp-execution-policy-api
    -> ksp-program-api
    -> ksp-core-lib

ksp-execution-lib
    -> ksp-program-api
    -> ksp-execution-policy-api
    -> ksp-wallet-lib
    -> ksp-onchain-transport-lib
    -> ksp-logging-lib
    -> ksp-core-lib

ksp-execution-lib ne dépend pas de ksp-program-lib et reçoit fondamentalement une opération déjà préparée conforme à ksp-program-api.

Policy obligatoire et multi-checkpoints

Toute exécution réelle reçoit explicitement une implémentation de ksp-execution-policy-api.

La policy peut être consultée à plusieurs checkpoints (préflight, après simulation, avant submission ou autres points réellement justifiés). Le contrat exact ne doit pas imposer plus de callbacks que nécessaire.

La policy peut autoriser/refuser/ajouter des requirements, mais n'appelle ni wallet, ni transport, ni UI.

Composition supérieure

Le caller fournit notamment :

  • réseau/provider/transport sélectionné ;
  • wallet/signers sélectionnés ;
  • options d'exécution.

ksp-execution-lib vérifie/compose ces éléments avec les contraintes techniques de PreparedProgramExecution et les contraintes supplémentaires de policy.

Simulation / signature / submission / confirmation

  • primitive simulation/submission/status -> ksp-onchain-transport-lib ;
  • orchestration de ces étapes -> ksp-execution-lib ;
  • primitive signature -> ksp-wallet-lib ;
  • résolution/coordination des signers -> ksp-execution-lib.

Retry

Retry technique d'un appel réseau identique -> transport.

Retry qui change le lifecycle (nouveau blockhash, reconstruction, resimulation, resignature, resubmission) -> execution, éventuellement limité par policy.

Approbation externe

Une policy peut produire un requirement d'approbation externe. ksp-execution-lib peut retourner un état suspendu/reprenable ; l'UI obtient l'approbation sans être appelée directement par la policy ou l'execution lib.

Store

ksp-execution-policy-api -X-> ksp-store-api
ksp-execution-lib        -X-> ksp-store-api
ksp-execution-lib        -X-> ksp-store-lib

La persistence d'un ExecutionOutcome est une composition supérieure.


Graphe Wallet

ksp-wallet-lib
    -> ksp-core-lib

ksp-wallet-lib peut consommer config/logging si nécessaire à son implémentation, sans que ces dépendances deviennent partie obligatoire du contrat wallet.

Interdictions :

ksp-wallet-lib -X-> ksp-program-lib
ksp-wallet-lib -X-> ksp-execution-lib
ksp-wallet-lib -X-> ksp-store-lib

Le wallet fournit les capacités de clés/signature ; il n'orchestre pas une opération Solana.


Graphe Transport

On-chain

ksp-onchain-transport-lib
    -> ksp-core-lib

Il peut consommer config/logging lorsque nécessaire.

Il peut posséder plusieurs familles internes :

HTTP RPC
WS RPC
Helius advanced
Yellowstone
...

sans créer ksp-onchain-transport-api.

Modèles homogènes

Chaque famille provider convertit ses réponses propriétaires vers des modèles KSP homogènes à l'intérieur de la frontière transport.

Exemple conceptuel :

Solana RPC ------\
Helius -----------+--> transport raw model
Yellowstone ------/

Ces modèles :

  • sont indépendants du store ;
  • conservent le raw et la provenance nécessaire ;
  • sont faciles à convertir vers les modèles persistants.

Interdiction ferme :

ksp-onchain-transport-lib -X-> ksp-store-api
ksp-onchain-transport-lib -X-> ksp-store-lib

Off-chain

ksp-offchain-transport-lib
    -> ksp-core-lib

Config/logging sont autorisés selon le besoin.

La crate reste volontairement hétérogène. Aucun ksp-offchain-transport-api global n'est introduit.


Graphe Materialization

ksp-materializer-api
    -> ksp-core-lib
    -> ksp-program-api

ksp-materializer-lib
    -> ksp-materializer-api
    -> ksp-program-api
    -> ksp-core-lib

ksp-interface-lib peut être consommée par une implémentation de materializer lorsqu'un contrat wire officiel est réellement requis, sans devenir une dépendance obligatoire de l'API.

Capacités

ksp-materializer-api doit permettre de distinguer conceptuellement :

GenericMaterializer
    D2 runtime/Core -> output générique D3

DomainProjector
    D3/canonical inputs -> output spécialisé D4

Les noms exacts ne sont pas figés.

Indépendance du Store

ksp-materializer-api -X-> ksp-store-api
ksp-materializer-lib -X-> ksp-store-api
ksp-materializer-lib -X-> ksp-store-lib

Un materializer transforme ; un worker/job/pipeline spécialisé convertit son output vers le DTO Store et le persiste.

Une implémentation externe peut produire un output générique D3 sans migration PostgreSQL spécialisée.


Graphe Store

ksp-store-api
    -> ksp-core-lib

ksp-store-lib
    -> ksp-store-api
    -> ksp-core-lib
    -> ksp-config-lib
    -> ksp-logging-lib

PostgreSQL est l'implémentation de référence.

Niveaux durables

D1 Raw
D2 Core canonique
D3 journal de matérialisation générique
D4 projections spécialisées

ksp-store-api possède les contrats persistants de ces niveaux sans dépendre des modèles runtime de Program/Materializer/Transport.

Interdictions :

ksp-store-api -X-> ksp-program-api
ksp-store-api -X-> ksp-materializer-api
ksp-store-api -X-> ksp-onchain-transport-lib

ksp-store-lib -X-> ksp-program-lib
ksp-store-lib -X-> ksp-materializer-lib
ksp-store-lib -X-> ksp-onchain-transport-lib

Les composants de composition réalisent les conversions explicites.

Replay

Les frontières de replay restent indépendantes :

D1 -> D2
D2 -> D3
D3 -> D4

Les jobs de replay consomment le Store et les processors appropriés ; ils ne sont pas des modes des workers live.

Notifications persistées

ksp-store-api possède le contrat canonique de notification lorsqu'une donnée durable est disponible.

live worker ----\
backfill job ----+--> PersistedDataAvailable (nom conceptuel)
import ----------/
replay ----------/

La notification est un signal de réveil et peut être perdue ou dupliquée.

Le consumer reconstruit toujours son backlog via le Store, les versions de processor et les marqueurs d'idempotence.

L'ordre est :

persist
commit
notify

Le payload privilégie une référence durable compacte.

Le contrat reste indépendant du mécanisme. PostgreSQL LISTEN/NOTIFY est retenu comme mécanisme initial de référence de wake-up, combiné à un polling périodique du backlog.


Graphe des pipelines spécialisés

Les pipelines réutilisent une frontière de processing sans prendre le lifecycle worker/job.

ksp-pipeline-raw-ingestion-lib
    -> ksp-onchain-transport-lib
    -> ksp-store-api
    -> ksp-core-lib
    -> ksp-logging-lib

ksp-pipeline-core-processing-lib
    -> ksp-program-api
    -> ksp-store-api
    -> ksp-core-lib
    -> ksp-logging-lib

ksp-pipeline-generic-materialization-lib
    -> ksp-materializer-api
    -> ksp-store-api
    -> ksp-core-lib
    -> ksp-logging-lib

ksp-pipeline-domain-projection-lib
    -> ksp-materializer-api
    -> ksp-store-api
    -> ksp-core-lib
    -> ksp-logging-lib

Interdictions :

pipelines -X-> ksp-store-lib
core-processing pipeline -X-> ksp-program-lib
materialization pipelines -X-> ksp-materializer-lib
pipelines -X-> ksp-worker-api
pipelines -X-> ksp-job-api

Les workers/jobs fournissent les implémentations officielles ou externes aux pipelines.


Graphe lifecycle Worker

ksp-worker-api
    -> ksp-core-lib

ksp-worker-control-lib
    -> ksp-worker-api
    -> ksp-core-lib

ksp-worker-control-lib ne dépend pas des workers concrets. Il fournit une gouvernance réutilisable au-dessus de l'API.

Les managers/apps/orchestrateurs composent les implémentations concrètes avec cette bibliothèque.

Interdictions :

ksp-worker-api -X-> ksp-job-api
ksp-worker-control-lib -X-> ksp-job-api

Le contrôle des jobs ne fuit pas dans la gouvernance workers.


Graphe lifecycle Job

ksp-job-api
    -> ksp-core-lib

Aucune ksp-job-control-lib n'est prévue actuellement.

Interdictions :

ksp-job-api -X-> ksp-worker-api
ksp-job-api -X-> ksp-worker-control-lib

Un futur orchestrateur peut utiliser workers et jobs séparément sans créer de superclass lifecycle commune.


Graphe des composants concrets d'acquisition/processing

ksp-worker-raw-retriever

ksp-worker-raw-retriever
    -> ksp-worker-api
    -> ksp-pipeline-raw-ingestion-lib
    -> ksp-onchain-transport-lib
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

Il pilote l'acquisition live, puis transmet chaque modèle transport homogène au pipeline raw ingestion.

Il possède une capacité spécifique de hot reconfiguration et distingue configuration desired/effective.

ksp-job-backfill

ksp-job-backfill
    -> ksp-job-api
    -> ksp-pipeline-raw-ingestion-lib
    -> ksp-onchain-transport-lib
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

Il pilote pagination/range/checkpoint historique puis transmet les modèles acquis au même pipeline D1 que W1.

ksp-worker-core-processor

ksp-worker-core-processor
    -> ksp-worker-api
    -> ksp-pipeline-core-processing-lib
    -> ksp-program-lib                # composition officielle
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

Une composition alternative peut fournir une implémentation externe compatible avec ksp-program-api.

ksp-job-replay-core

ksp-job-replay-core
    -> ksp-job-api
    -> ksp-pipeline-core-processing-lib
    -> ksp-program-lib ou implémentation compatible
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

Worker live et replay partagent donc exactement la logique D1 -> D2.

ksp-worker-generic-materializer

ksp-worker-generic-materializer
    -> ksp-worker-api
    -> ksp-pipeline-generic-materialization-lib
    -> ksp-materializer-lib           # composition officielle
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

ksp-job-replay-generic-materialization

ksp-job-replay-generic-materialization
    -> ksp-job-api
    -> ksp-pipeline-generic-materialization-lib
    -> ksp-materializer-lib ou implémentation compatible
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

ksp-worker-domain-projector

ksp-worker-domain-projector
    -> ksp-worker-api
    -> ksp-pipeline-domain-projection-lib
    -> ksp-materializer-lib           # projectors officiels
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

ksp-job-replay-domain-projection

ksp-job-replay-domain-projection
    -> ksp-job-api
    -> ksp-pipeline-domain-projection-lib
    -> ksp-materializer-lib ou implémentation compatible
    -> ksp-store-lib
    -> ksp-config-lib
    -> ksp-logging-lib

Backlog / notification

Tous les workers de processing :

LISTEN/NOTIFY wake-up
        +
periodic polling
        |
        v
Store backlog query
        |
        v
claim/lease bounded batch
        |
        v
pipeline
        |
        v
outputs + processing outcome

La notification ne remplace jamais le Store.


Graphe applications / services / control plane

Worker service autonome

package ksp-worker-<role>

library target
    -> ksp-worker-api
    -> pipeline spécialisé
    -> implémentations KSP nécessaires

binary target
    -> library target du même package
    -> ksp-config-lib
    -> ksp-logging-lib
    -> bootstrap/service control adapter

Le binaire ne contient pas une seconde logique de processing.

App worker spécialisée

ksp-app-worker-<role>-desk
    -> ksp-worker-control-lib
    -> ksp-worker-api
    -> future remote proxy / IPC adapter

L'app ne dépend pas du worker concret pour réimplémenter son lifecycle interne.

Data plane

transport -> D1 -> D2 -> D3 -> D4

Control plane

specialized app/tool
        |
        v
ksp-worker-control-lib
        |
        v
ksp-worker-api
        |
        v
local handle / future remote proxy

Jobs restent parallèlement sous ksp-job-api.

Interdictions :

worker A -X-> worker B
control plane -X-> payload transfer D1/D2/D3/D4
scenario demo app -X-> duplicate scenario workflow

Aucun ksp-ipc-api générique n'est retenu maintenant.


Scenarios et applications demo

La logique du scénario s'exécute dans la crate scenario elle-même et reste appelable sans desktop.

Une crate scénario peut composer les capacités nécessaires à son domaine :

ksp-scenario-<domain>-lib
    -> ksp-program-api
    -> ksp-program-lib
    -> ksp-execution-policy-api
    -> ksp-execution-lib
    -> ksp-wallet-lib
    -> ksp-onchain-transport-lib
    -> ksp-config-lib

Toutes ces dépendances ne sont pas obligatoires pour chaque scénario.

La crate scénario peut implémenter une policy Devnet adaptée à son besoin.

L'application correspondante reste une interface :

ksp-app-scenario-<domain>-devnet-desk-demo
    -> ksp-scenario-<domain>-lib

Le scenario ne réside jamais dans l'application.

Elle peut dépendre de bibliothèques KSP d'interface/configuration strictement nécessaires à son UI, mais ne réimplémente ni scénario ni policy métier.


Orchestrateur futur — concept uniquement

Un futur orchestrateur pourra être au-dessus des contrôles spécialisés, mais aucune crate n'est retenue comme livrable actuel.

Conceptuellement :

orchestrator
    -> ksp-worker-control-lib
    -> ksp-job-api              # séparément, si coordination de jobs nécessaire
    -> autres contrôles spécialisés au besoin

Cette dépendance simultanée ne crée pas d'API lifecycle commune.

L'orchestrateur coordonne ; il ne fusionne pas les modèles worker/job.


Dépendances explicitement non retenues

Les composants suivants ne sont pas introduits dans le graphe actuel :

ksp-api-lib
ksp-onchain-transport-api
ksp-offchain-transport-api
ksp-wallet-api
ksp-scenario-api
ksp-job-control-lib
ksp-pipeline-lib
ksp-data-api

Ils pourront être réévalués uniquement si un besoin concret démontre que la frontière actuelle ne suffit plus.


Contrôle des cycles

Le graphe impose notamment :

core
  <- interface
  <- program-api
  <- materializer-api

store-api
  <- store-lib

worker-api
  <- worker-control

job-api
  <- concrete jobs

et interdit toute boucle inverse depuis les couches basses vers leurs consommateurs.

Les conversions entre Program/Materializer/Store ne sont pas résolues par des dépendances croisées mais par des composants de composition explicites.


Questions restantes

Les grandes frontières de dépendances sont désormais fixées. Restent à résoudre avec les premières implémentations :

  • types Rust exacts de ksp-program-api, ksp-materializer-api et ksp-store-api ;
  • représentation ouverte/persistable des résultats Program ;
  • schémas SQL, transactions, claims/leases et pagination PostgreSQL ;
  • mécanisme IPC du premier manager de worker autonome ;
  • injection du contexte nécessaire aux DomainProjector stateful ;
  • éventuel orchestrateur commun uniquement si un besoin opérationnel concret apparaît.