jonnifyelixir
F6 · Phoenix Framework / F6.03 · ecto

F6 · the architecture · module 3

Ecto: schemas, changesets & queries

Ecto is Elixir’s database toolkit, and it does its work in three clear pieces. A schema maps a database table to a plain Elixir struct, so a row is a %Course{} in your code. A changeset is a pure pipeline that casts and validates incoming data before any write, carrying its own valid? flag and errors. And the repo runs composable queries and persists changesets. The discipline that keeps F5 intact is where Ecto lives: behind the engine’s port — the same Portal.EventStore behaviour from the F5.09 lab, now backed by its Postgres adapter — so the domain core still names no database. Ecto is an adapter detail, not a layer the Portal logic depends on.

This module covers Ecto’s three pieces and the boundary that contains them: schemas and migrations, changesets and validation, and queries and the repo behind the port.

The changeset pipeline

PARAMS %{"title" => "Algebra", "credits" => 4} CAST permit & coerce to typed fields VALIDATE required & number rules CONSTRAINT unique_constraint :title REPO Repo.insert(changeset) valid?: true — row inserted

%{title: "Algebra", credits: 4}
cast → validate → constraint all pass — the Repo writes the row.

Three pieces, one adapter

Schema, changeset, and repo divide the work cleanly: the schema describes the shape, the changeset guards the write, and the repo talks to the database. Together they are the implementation of one driven port from F5 — the core asks the port to read or append, and Ecto is how the Postgres adapter answers. Select a piece to see its role.

The Ecto layer · select a piece

SCHEMA schema "courses" do field ... end — a table as a struct F6.03.1 CHANGESET cast |> validate |> %Changeset{valid?: ...} — guard the write F6.03.2 QUERY / REPO Repo.all(query) · Repo.insert(changeset) — behind the port F6.03.3

piece: Schema

is: a table ↔ a struct

Ecto is not an ORM that hides the database; it is three explicit tools you compose. And because they sit behind the F5 port, swapping the in-memory adapter for Postgres is a config change, not a rewrite of the Portal.

Three deep dives

In order: how migrations and schemas describe a table and its struct, with a Snowflake primary key; how a changeset casts and validates before a write; and how the repo runs queries and persists, kept behind the engine’s port.

F6.03.1
Schemas & migrations
A migration creates the table; a schema maps it to %Course{}. The primary key is a Snowflake bigint we mint, not a serial.
F6.03.2
Changesets & validation
cast → validate → %Changeset{}: a pure pipeline that guards every write and carries its own errors.
F6.03.3
Queries & the repo
Composable queries and Repo calls, kept behind the F5.09 port so the core never names the database.

F5.09 · the port

The engine reads and appends through Portal.EventStore, a behaviour with two adapters.

F6.03 · the Postgres adapter

Ecto implements that port for production — schemas, changesets, and queries behind it.

Start with schemas and migrations, then changesets and validation, then queries and the repo. This module follows F6.02 — routing, controllers & plugs — and the next, F6.04, draws the boundary between Phoenix contexts and the F5 facade.

References

Ecto’s three pieces in reference form: the schema, the changeset, the query, and the repo.

Sources

Related in this course