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 → 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
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
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.
Portal facade are one idea; how they layer, and where Ecto sits when the port already hides it.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
- Phoenix — Contexts — dedicated modules that group and expose functionality.
- Phoenix — mix phx.gen.context — the generator that scaffolds a context, schema, and tests.
- Martin Fowler — Bounded Context — the domain-driven design idea behind the name.
- Ecto — Multi — grouping writes inside a context transactionally.
Related in this course
- F6.04.1 · Context boundaries
- F6.04.2 · Contexts vs the F5 facade
- F6.04.3 · Composing contexts
- F6.03 · Ecto — the schemas and Repo a context keeps private.
- F6 · Phoenix Framework