crabster

Technical architecture

{% raw %}

This document describes the proposed technology choices for Crabster and the code it generates. Each major choice is tagged [ADR] and can be revised — but any revision must be documented as such (status, alternatives considered, reason for the change).

1. Overview🔗

Crabster is made of two distinct parts:

Crabster generates a project that depends on it in no way at runtime

2. The generator (CLI)🔗

2.1 Language and distribution — [ADR]🔗

2.2 CDL parsing🔗

2.3 Template engine — [ADR]🔗

2.4 Blueprint mechanism (extensibility)🔗

The mechanism:

3. The generated project (backend)🔗

3.1 Web framework — [ADR]🔗

3.2 ORM and data access — [ADR]🔗

3.3 Migrations🔗

3.4 Authentication and security — [ADR]🔗

3.5 API documentation — [ADR]🔗

3.6 Validation🔗

3.7 Configuration🔗

3.8 Errors🔗

3.9 Tests🔗

3.10 Observability🔗

3.11 Containerization and deployment🔗

3.12 CI/CD🔗

4. Intermediate Representation (IR) — the central pivot🔗

The IR is the stable contract between "CDL parser" and "generation engine". It must stay agnostic of CDL syntax to allow, eventually, other input sources (importing an existing SQL schema, importing an OpenAPI spec). Sketch of the structure (detailed in Phase 3):

struct DomainModel {
    records: Vec<Record>,
    enums: Vec<Enumeration>,
    service: Option<Service>,   // name, database, port
}

struct Record {
    name: String,
    fields: Vec<Field>,         // a field may be a reference
    filterable: bool,
}

struct Field {
    name: String,
    kind: FieldType,            // Scalar | Enum(name) | Reference(name)
    optional: bool,             // mandatory by default
    unique: bool,
    length: Option<Bounds>,
    range: Option<Bounds>,
    matches: Option<String>,
}

There is no relationship type: a reference is a field, and the side declaring it is the side holding the foreign key. That absence removes all the code that would otherwise decide which side the column belongs on.

5. Crabster repository layout (proposed monorepo)🔗

crabster/
├── crates/
│   ├── crabster-cli/       # binary, clap subcommands
│   ├── crabster-cdl/       # CDL parser + IR
│   ├── crabster-codegen/   # Tera engine + module orchestration
│   │   └── templates/      # templates for the generated code — under the crate,
│   │       ├── core/       #   not at the root: `cargo package` only ships what is
│   │       ├── record/     #   below the package root, and `include_dir!` on a
│   │       ├── auth-jwt/   #   missing directory is a compile error
│   │       ├── db-postgres/ db-mysql/ db-sqlite/
│   │       ├── docker/
│   │       └── ci-github-actions/
│   └── crabster-shared/    # shared utilities (if needed at generated-app runtime)
├── examples/               # reference CDL models, generated and exercised in CI
└── docs/

6. The incremental-update challenge ("upgrade")🔗

The hardest problem in this space: merging generated code the user has since modified. Planned approach:

7. Alternatives explicitly ruled out (and why)🔗

ChosenAlternative ruled outReason
AxumRocketMacros too "magic" for generated code meant to be easily read/modified
AxumActix-webActor model adds unnecessary conceptual complexity; Axum/tower is sufficient
SeaORMDieselNative async API, familiar entity/relationship model
SeaORMRaw SQLxSQLx remains a viable option for a "hand-written SQL" blueprint, but SeaORM better matches the default "entities" experience expected
TeraAskamaRuntime-loaded templates are required for external blueprints
Stateless JWTSessionAPI-only use case is the priority; session support deferred
{% endraw %}