Aller au contenu

Architecture — vue d'ensemble

Guide pour développeurs qui veulent comprendre l’organisation du code, les règles de dépendance et les clusters fonctionnels d’arka-deck.


arka-deck est une application hexagonale TypeScript/Node.js. Le cœur ne dépend de rien d’externe — toutes les dépendances pointent vers lui, jamais l’inverse.

┌─────────────────────────────────────────────────────────────┐
│ adapters/ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ inbound/ │ │ outbound/ │ │
│ │ cli/ │ │ filesystem/ │ │
│ │ web/ │ │ http/ │ │
│ │ server/ │ │ chat/ │ │
│ │ ui/ (React) │ │ providers/ │ │
│ └──────────────────┘ │ events/ │ │
│ │ process/ │ │
│ │ secrets/ │ │
│ │ system/ │ │
│ └──────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ composition/ │ │
│ │ core-container.ts web-container.ts │ │
│ └─────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ core/ │ │
│ │ domain/ ports/ use-cases/ │ │
│ │ orchestration/ │ │
│ └─────────────────────────────────────────┘ │
│ ┌─────────────┐ ┌──────────────────────┐ │
│ │ addons/ │ │ providers/ │ │
│ │ workers/ │ │ claude_code/ │ │
│ └─────────────┘ │ codex_cli/ │ │
│ │ google-genai/ │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

DossierRôle
core/Domaine pur — ports, use-cases, types domain, orchestration
adapters/inbound/Points d’entrée — CLI et serveur web (Fastify + UI React/Vite)
adapters/outbound/Implémentations I/O — filesystem, HTTP, chat, providers, secrets, système
composition/Assemblage des dépendances — DI manuel, containers
addons/Modules autonomes event-driven — addons préinstallés first-party
providers/Implémentations runtime LLM — claude_code, codex_cli, google-genai
workers/Workers headless 1-shot — manifest + prompt
bin/Binaire CLI arka-deck
scripts/Scripts utilitaires (release, prepare-dist, gates CI)
e2e/Tests end-to-end Playwright

Le cœur hexagonal. Aucune dépendance vers adapters/, addons/, composition/ ou providers/.

core/
├── domain/ # Types et règles métier pures
│ ├── chat/ # Transcript, StreamEvent, sessions
│ ├── catalogue/ # Profils, installs, agents
│ ├── events/ # ArkaEvent — union discriminée de tous les events bus
│ ├── project/ # Projet, workspace
│ ├── providers/ # Manifests provider
│ ├── squad/ # Composition d'équipe
│ └── workers/ # Manifests worker
├── ports/
│ ├── inbound/ # Contrats use-cases (for-chat.ts, for-projects.ts…)
│ └── outbound/ # Contrats I/O (event-bus.ts, filesystem.ts, clock.ts…)
├── use-cases/ # Logique applicative — implémente les ports inbound
└── orchestration/ # Orchestration multi-agents (LangGraph)

Les ports inbound (core/ports/inbound/for-*.ts) définissent les contrats que les use-cases exposent aux adapters :

  • for-chat.ts — lancer, reprendre, envoyer un tour
  • for-projects.ts — CRUD projets et workspaces
  • for-catalogue.ts — charger et installer des agents depuis le Cortex
  • for-workers.ts — déclencher un worker
  • for-preferences.ts — lire et écrire les préférences
  • et plusieurs autres — la liste exhaustive est dans ports-inbound.md (à venir).

Les ports outbound (core/ports/outbound/*.ts) définissent les contrats que les adapters implémentent :

  • event-bus.ts — bus d’événements typé in-process
  • filesystem.ts — lecture/écriture fichiers (avec allowlist)
  • chat-runtime.ts — exécution d’un tour LLM
  • before-turn-augmenter.ts — injection de contexte avant un tour
  • clock.ts, id-generator.ts — utilitaires

Les frontières sont vérifiées mécaniquement par ESLint à chaque lint :

SourceInterdit d’importer
core/**adapters/**, composition/**, addons/**, providers/**
core/domain/**core/use-cases/**
adapters/inbound/**adapters/outbound/**
adapters/**addons/**

La composition (composition/) est le seul endroit autorisé à assembler tout ensemble.


Le container de composition instancie tous les adapters, les use-cases et les addons, puis les câble via injection de dépendances manuelle.

  • core-container.ts — instancie le cœur (event bus, stores, use-cases)
  • web-container.ts — ajoute le serveur Fastify, les routes et l’UI

C’est le seul fichier autorisé à importer depuis addons/, providers/ et adapters/ simultanément.


Le bus est un module cœur (core/ports/outbound/event-bus.ts), instancié une fois au boot dans core-container.ts. Il transporte des events typés in-process entre tous les modules.

interface EventBus {
publish(event: ArkaEvent): Promise<void>; // attend tous les handlers
publishAsync(event: ArkaEvent): void; // non-bloquant
subscribe<T extends ArkaEventType>(
type: T,
handler: EventHandler<T>,
): Unsubscribe;
}

La liste complète des events est dans core/domain/events/arka-event.ts (source de vérité). Convention de nommage : <domain>.<entity>.<action> kebab-case. Voir event-bus pour la documentation complète.


Chaque addon est un module autonome dans son propre sous-dossier :

addons/<name>/
├── manifest.json # métadonnées déclaratives
└── src/
├── index.ts # fonction register + re-exports publics
├── domain/ # types domain locaux à l'addon
├── ports/
│ ├── for-<name>.ts # port inbound propre à l'addon
│ └── *.ts # ports outbound (stores, clients HTTP)
├── adapters/ # implémentations des ports outbound
└── use-cases/ # logique applicative de l'addon

Les addons peuvent importer depuis core/ (ports outbound, types domain) mais pas depuis adapters/ ni depuis d’autres addons.

Pour créer un addon, voir le tutoriel ../extension/ecrire-un-addon.md (à venir).


Les providers implémentent le port outbound chat-runtime.ts pour un LLM donné :

providers/
├── claude_code/ # SDK @anthropic-ai/claude-agent-sdk
├── codex_cli/ # Codex CLI
└── google-genai/ # Google Gemini

Chaque provider expose un runtime.ts qui satisfait le contrat ChatRuntime et un manifest.json ou équivalent qui déclare ses métadonnées (modèles disponibles, capacités).


Les workers sont des processus LLM headless déclarés par manifest.json. Ils ne sont pas exécutés directement — c’est le système de workers d’arka-deck qui les invoque via le port for-workers.ts.

workers/<name>/
├── manifest.json # déclaration (input, output, runtime, prompt_source)
├── prompt.json # prompt local (optionnel — sinon via Cortex)
└──

Pour concevoir un worker, voir ../extension/ecrire-un-worker.md (à venir).


Les décisions structurantes sont documentées dans ../../adr/ :

ADRSujet
0001Architecture hexagonale
0002Contrat addon
0003Pattern Materializer
0004Distribution GitHub uniquement
0005Secrets AES-GCM local
0006Protection anti-SSRF providers

CoucheTechnologie
RuntimeNode.js ≥ 20.19.0, TypeScript (strict)
Serveur HTTPFastify 5.8.5
UIReact 18, Vite, Tailwind CSS, React Query
Stockage localSQLite (better-sqlite3), fichiers JSON
SecretsAES-256-GCM dans ~/.arka-deck/
ValidationZod
SDK Claude@anthropic-ai/claude-agent-sdk 0.2.129
OrchestrationLangChain / LangGraph
TestsVitest (unit), Playwright (E2E)
LintingESLint (max-warnings=0), Prettier