jonnifyelixir
F6 · Phoenix Framework / F6.04 · contexts

F6 · the architecture · module 4

Contexts & domain design

The Phoenix guides put it bluntly: Phoenix is not your application. The framework is the web interface; the application is a set of contexts — dedicated modules that group related functionality and expose it through a small, deliberate public API. A context names a slice of the domain (“the catalog,” “enrollment,” “accounts”), owns the schemas and queries inside it, and hides the Repo behind functions like Catalog.list_courses/0. Callers — controllers, LiveViews, other contexts — depend on that API, never on its internals. If that sounds familiar, it should: it is the same boundary the F5 Portal facade already draws. This module reconciles the two vocabularies and makes the boundary load-bearing — a context that leaks its schemas is a boundary in name only, and the cost shows up the first time two slices need to change independently.

Three dives: how to draw a context boundary by cohesion and keep its schemas private; how a Phoenix context relates to the hexagonal facade and its port from F5 and F6.03; and how to compose across contexts without one reaching into another.

Through the API, or reaching in

Controller a caller Enrollment CONTEXT PUBLIC API enroll/2 · list_courses/1 PRIVATE · hidden inside Enrollment schema · queries the Repo boundary breached

Controller → Enrollment.enroll/2
The call enters through the one public door; the schemas and Repo stay sealed.

Three ways to look at a boundary

A context can be described three ways, and this module takes each in turn. As a boundary, it groups related functionality and decides what is public. As a facade, it is the single API a slice exposes — the one entry callers are allowed to touch. And in composition, contexts relate to each other only through those APIs, so the domain is a graph of small public surfaces rather than one tangled module. Select a view.

A context, three ways · select one

BOUNDARY group related functionality · decide what is public F6.04.1 FACADE one public API over a slice — the same idea as Portal (F5) F6.04.2 COMPOSITION contexts call contexts through public APIs — no reaching in F6.04.3

view: Boundary

is: group & expose a slice

A context is not a layer and not a table — it is a capability. “Enroll a student, list a student’s courses” is one context; the rows behind it are an implementation detail it is free to change.

A context map

Drawn out, a Phoenix application is a handful of contexts, each a public API with private schemas and the Repo sealed inside. The web layer talks to the APIs; the APIs talk to each other only where a real dependency exists. The map below is the Portal’s: a Catalog of courses, an Enrollment slice that depends on it, and an independent Accounts context — with Ecto and the database hidden underneath all three.

The Portal as a context map

WEB LAYER · controllers & LiveViews (F6.01–F6.02) Catalog PUBLIC API list_courses/0 · get_course!/1 create_course/1 private: Course, Lesson schemas Enrollment PUBLIC API enroll/2 · courses_of/1 progress_of/1 private: Enrollment schema Accounts PUBLIC API get_user/1 register/1 independent depends on → (one way) PERSISTENCE · Ecto + the Repo behind the F6.03 port private to the contexts — no caller above the line names Ecto or Repo

Read top to bottom, every arrow crosses a public API. Nothing in the web layer knows a schema name, and Enrollment reaches Catalog only through Catalog’s functions — the property the rest of this module defends.

Three deep dives

In order: drawing the boundary and keeping schemas private; reconciling the Phoenix context with the F5 facade and its port; and composing contexts through their public APIs.

F6.04.1
Context boundaries
What a context exposes and what it hides; drawing the line by cohesion; why a schema is private to its context.
F6.04.2
Contexts vs the F5 facade
The context and the Portal facade are one idea; how they layer, and where Ecto sits when the port already hides it.
F6.04.3
Composing contexts
One context calling another’s API, a one-way dependency graph, and a with pipeline that returns the closed error.

Phoenix says

“A context is a module that groups related functionality and exposes a public API.”

F5 already built

The Portal facade — one public API over a slice of the domain. Same boundary.

Start with context boundaries, then contexts vs the F5 facade, then composing contexts. This module follows F6.03 — Ecto — and the next, F6.05, renders these contexts’ data with HEEx and components.

References

The Phoenix contexts guide, its generators, and the domain-driven idea the term comes from.

Sources

Related in this course