<p align="center">
<img src="assets/logo.svg" alt="Caravela" width="180" height="180"/>
</p>
# Caravela
*Declare your domain. Sail with the generated code.*
A schema-driven, composable full-stack framework for Phoenix projects.
You describe a domain (entities, fields, relations) as an Elixir DSL;
Caravela generates Ecto schemas, migrations, Phoenix contexts,
controllers, LiveViews, and typed Svelte components.
> **Status — Phase 1.** The DSL, the compiler, and the Ecto-schema +
> migration generators are in place. Contexts, LiveView, Svelte, and
> Flow orchestration land in later phases.
## Installation
Add `caravela` to your deps in `mix.exs`:
```elixir
def deps do
[
{:caravela, "~> 0.1.0"}
]
end
```
Phoenix and `ecto_sql` are assumed to already be present in the host
app; Caravela generates code against them.
## Quick start
### 1. Declare a domain
```elixir
# lib/my_app/domains/library.ex
defmodule MyApp.Domains.Library do
use Caravela.Domain
entity :authors do
field :name, :string, required: true
field :bio, :text
field :born, :date
end
entity :books do
field :title, :string, required: true, min_length: 3
field :isbn, :string, format: ~r/^\d{13}$/
field :published, :boolean, default: false
field :price, :decimal, precision: 10, scale: 2
end
entity :publishers do
field :name, :string, required: true
field :country, :string
end
relation :authors, :books, type: :has_many
relation :books, :publishers, type: :belongs_to
end
```
### 2. Generate schemas and a migration
```bash
mix caravela.gen.schema MyApp.Domains.Library
# * created priv/repo/migrations/20260417120000_create_library_tables.exs
# * created lib/my_app/library/author.ex
# * created lib/my_app/library/book.ex
# * created lib/my_app/library/publisher.ex
```
The generator drops files where a standard Phoenix app expects them.
Every file is plain Phoenix / Ecto code — no runtime magic.
Pass `--dry-run` to preview, or `--force` to overwrite existing files
without prompting.
### 3. Migrate
```bash
mix ecto.migrate
```
The generated migration creates tables in dependency order and adds
foreign-key indexes. Required fields get `null: false`; required
`belongs_to` relations become `on_delete: :delete_all` (non-required
become `:nilify_all`).
## DSL reference
### `entity :<name> do ... end`
Declares one entity (one table). The name is plural (`:books`); the
generator derives a singular module name (`Book`), a plural table name
(`library_books`), and a path (`lib/<app>/library/book.ex`).
### `field :<name>, <type>, opts`
| option | applies to | effect |
|---------------|-------------------|-------------------------------|
| `required` | any | `null: false` + `validate_required` |
| `default` | any | column default |
| `min`, `max` | numeric | `validate_number` |
| `min_length`, `max_length` | string-like | `validate_length` |
| `format` | string-like | `validate_format` (regex) |
| `precision`, `scale` | numeric | decimal precision/scale |
Recognised types: `:string`, `:text`, `:integer`, `:bigint`, `:float`,
`:decimal`, `:boolean`, `:date`, `:time`, `:naive_datetime`,
`:utc_datetime`, `:binary`, `:binary_id`, `:uuid`, `:map`, `:json`,
`:jsonb`.
### `relation :<from>, :<to>, type: <t>`
`t` is one of `:has_many`, `:has_one`, `:belongs_to`, `:many_to_many`.
Declare either side of a relationship — Caravela infers the other.
## Compile-time validations
The DSL is validated before any code is generated. Each rule raises
a `CompileError` with a file/line pointing at the declaration:
1. Unknown field types (`:widget` etc.)
2. Numeric constraints on non-numeric fields (and vice versa)
3. Duplicate entity names
4. Relations referencing undeclared entities
5. Incompatible cardinality (e.g. both sides `:has_many`)
6. Circular chains of required `belongs_to` (unsatisfiable inserts)
## Primary keys and ids
Every generated schema uses `:binary_id` (UUID) primary and foreign
keys. No enumeration attacks, no sequence exhaustion, and Ecto-native.
## What's in Phase 1
- `Caravela.Domain` DSL: `entity`, `field`, `relation`
- `Caravela.Compiler` with six validations
- `Caravela.Gen.EctoSchema` — Ecto schema generator (with changeset)
- `Caravela.Gen.Migration` — Ecto migration generator (topologically
sorted, FK indexes)
- `mix caravela.gen.schema MyApp.Domains.<Module>`
## Roadmap
Later phases add Phoenix contexts, JSON controllers, LiveView modules
that mount Svelte components via LiveSvelte, typed Svelte component
generation, Absinthe/GraphQL schema generation, and a GenServer-backed
flow runtime for composable async workflows.
## License
Caravela is licensed under the
[Mozilla Public License 2.0](LICENSE) (MPL-2.0). In short:
- You can use Caravela — including in closed-source Phoenix applications
— freely.
- If you modify a Caravela source file and distribute it, that file must
stay under MPL-2.0, with authorship and copyright notices intact.
- You may not strip attribution or pass this work off as your own.
See [NOTICE](NOTICE) for the full attribution and anti-plagiarism
statement.
## Supporting the project
Caravela is built in the open and free to use. If it saves you time or
ships something you're proud of, please consider sponsoring its
development — donation channels (GitHub Sponsors, Open Collective, etc.)
will be linked here once set up.
Every contribution, from a PR to a coffee, helps keep the sails full.