crabster

Architecture technique

{% raw %}

Ce document décrit les choix technologiques proposés pour Crabster et le code qu'il génère. Chaque choix majeur est marqué [ADR] et peut être révisé — mais doit alors être documenté comme tel (statut, alternatives considérées, raison du changement).

1. Vue d'ensemble🔗

Crabster est composé de deux parties distinctes :

Crabster engendre un projet qui ne dépend de lui en rien à l'exécution

2. Le générateur (CLI)🔗

2.1 Langage et distribution — [ADR]🔗

2.2 Parsing CDL🔗

2.3 Moteur de templates — [ADR]🔗

2.4 Mécanisme de blueprints (extensibilité)🔗

Le mécanisme :

3. Le projet généré (backend)🔗

3.1 Framework web — [ADR]🔗

3.2 ORM et accès aux données — [ADR]🔗

3.3 Migrations🔗

3.4 Authentification et sécurité — [ADR]🔗

3.5 Documentation API — [ADR]🔗

3.6 Validation🔗

3.7 Configuration🔗

3.8 Erreurs🔗

3.9 Tests🔗

3.10 Observabilité🔗

3.11 Conteneurisation et déploiement🔗

3.12 CI/CD🔗

4. Modèle intermédiaire (IR) — pivot central🔗

L'IR est le contrat stable entre "parser CDL" et "moteur de génération". Il doit rester agnostique de la syntaxe CDL pour permettre, à terme, d'autres sources d'entrée (import d'un schéma SQL existant, import OpenAPI). Esquisse de structure (détaillée en Phase 3) :

struct DomainModel {
    records: Vec<Record>,
    enums: Vec<Enumeration>,
    service: Option<Service>,   // nom, base de données, port
}

struct Record {
    name: String,
    fields: Vec<Field>,         // un champ peut être une référence
    filterable: bool,
}

struct Field {
    name: String,
    kind: FieldType,            // Scalar | Enum(nom) | Reference(nom)
    optional: bool,             // obligatoire par défaut
    unique: bool,
    length: Option<Bounds>,
    range: Option<Bounds>,
    matches: Option<String>,
}

Il n'y a pas de type « relation » : une référence est un champ, et le côté qui la déclare est celui qui porte la clé étrangère. Cette absence supprime tout le code qui aurait servi à déterminer de quel côté placer la colonne.

5. Structure du dépôt Crabster (monorepo proposé)🔗

crabster/
├── crates/
│   ├── crabster-cli/       # binaire, sous-commandes clap
│   ├── crabster-cdl/       # parseur CDL + IR
│   ├── crabster-codegen/   # moteur Tera + orchestration des modules
│   │   └── templates/      # templates du code généré — sous le crate, pas à la
│   │       ├── core/       #   racine : `cargo package` n'embarque que ce qui est
│   │       ├── record/     #   sous la racine du paquet, et `include_dir!` sur un
│   │       ├── auth-jwt/   #   répertoire absent est une erreur de compilation
│   │       ├── db-postgres/ db-mysql/ db-sqlite/
│   │       ├── docker/
│   │       └── ci-github-actions/
│   └── crabster-shared/    # utilitaires partagés (si nécessaire au runtime généré)
├── examples/               # modèles CDL de référence, générés et exercés en CI
└── docs/

6. Le défi de la mise à jour incrémentale ("upgrade")🔗

C'est le point le plus délicat du domaine : fusionner du code généré que l'utilisateur a modifié. Approche envisagée :

7. Alternatives explicitement écartées (et pourquoi)🔗

Choix retenuAlternative écartéeRaison
AxumRocketMacros trop "magiques" pour du code généré à relire/modifier facilement
AxumActix-webModèle acteur ajoute une complexité conceptuelle non nécessaire ; Axum/tower suffit
SeaORMDieselAPI async native, modèle entité/relation familier
SeaORMSQLx brutSQLx reste une option bas niveau envisageable pour un blueprint "requêtes SQL à la main", mais SeaORM est plus proche de l'expérience "entités" attendue par défaut
TeraAskamaTemplates chargés au runtime indispensable pour les blueprints externes
JWT statelessSessionCas d'usage API-only prioritaire ; session différée
{% endraw %}