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:
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
# 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
end2. Generate schemas and a migration
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.exThe 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
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:
-
Unknown field types (
:widgetetc.) - Numeric constraints on non-numeric fields (and vice versa)
- Duplicate entity names
- Relations referencing undeclared entities
-
Incompatible cardinality (e.g. both sides
:has_many) -
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.DomainDSL:entity,field,relationCaravela.Compilerwith six validationsCaravela.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 (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 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.