Skip to content

Architecture

The single rule that keeps this template honest: business logic never leaks into FastAPI, Typer, or React. Those are delivery mechanisms. Swap any of them without touching the core.

flowchart TD
    subgraph core["packages/core — pure Python, zero deps"]
        D[Domain models<br/>Project, Note]
        P[Ports<br/>Repository protocols]
        S[Services<br/>use-cases]
    end

    subgraph adapters["Adapters"]
        BE[apps/backend<br/>FastAPI + SQLModel]
        CLI[apps/cli<br/>Typer &bull; opk]
        FE[apps/frontend<br/>React + typed client]
        DT[apps/desktop<br/>pywebview shell]
    end

    subgraph ext["Extensions"]
        PA[packages/plugin-api]
        LIC[packages/licensing]
        PL[extensions/*<br/>plugins]
    end

    S --> P
    BE -->|implements| P
    CLI -->|uses| S
    FE -->|HTTP| BE
    DT -->|in-process bridge| BE
    PL -->|entry points| PA
    BE -->|discovers| PA
    BE -->|gates on| LIC

The layers

Layer Responsibility Depends on
packages/core Domain models, Repository ports, services. No I/O. nothing
packages/plugin-api The Plugin contract + registry. nothing (runtime)
packages/licensing Entitlement: dev stub, Ed25519 signed tokens, file/HTTP providers. cryptography
apps/backend FastAPI HTTP adapter; owns SQLModel persistence + Alembic. core, plugin-api, licensing
apps/cli Typer CLI, task runner and control plane (opk). core, backend, plugin-api, licensing
apps/frontend React + Vite web UI over a generated typed client. backend (HTTP or desktop bridge)
apps/desktop pywebview shell; the same app called in-process (no HTTP). backend, frontend build
extensions/* Example plugins. plugin-api (+ backend for the paid one)

Why hexagonal here

Because the whole selling point is "one product, many faces". core is decoupled from both HTTP and the database:

  • The backend maps domain models to SQLModel rows in adapters/db — the core never sees a table.
  • The CLI builds the same services against the same repositories.
  • Core tests run with an in-memory fake repository — no database, no FastAPI, no network.

If the core weren't decoupled from HTTP and persistence, the template wouldn't prove its own thesis.