F5.01 · part 3 of 3
A web layer built for replacement
Starting thin only pays off if the thin part is easy to discard. The discipline is one rule: the web
layer may only call the engine’s boundary — dispatch/1 and
query/2 — and may never reach past it into the Portal’s internals. Hold that
line and the front end becomes a detail. In F6 the thin Plug server is replaced by Phoenix and LiveView, and
because Phoenix calls the very same functions, the engine does not change at all. This is orthogonality and ETC
— easier to change — made concrete: the part that varies (the web) is decoupled from the part that
stays (the Portal).
Two front ends, one engine. Toggle between today’s thin server and tomorrow’s Phoenix, and watch what stays fixed.
One engine, two front ends
The engine’s boundary is the seam. Whatever sits above it — a Plug router now, a Phoenix LiveView later — is only a caller, and callers are interchangeable when they all speak through the same two functions. Toggle the front end below; the engine box does not move.
The front end is swappable · select one
front end: thin server (Plug)
engine: unchanged
Decoupling is not a diagram; it is a rule you can break. “The web only calls the boundary” is the rule — keep it, and replacing the web is a swap, not a rewrite.
The same call, twice
Here is the same use case from both front ends. The Plug handler today and the LiveView event handler in F6 build the same command and make the same call; the difference is entirely in how each receives input and renders output, not in how it talks to the Portal. The engine cannot tell which one called it.
# today — the thin Plug handler (F5.01) post "/enroll" do Portal.Engine.dispatch(%EnrollLearner{user_id: conn.params["user"], course_id: conn.params["course"]}) end # tomorrow — a Phoenix LiveView event (F6), the same call def handle_event("enroll", %{"user" => u, "course" => c}, socket) do Portal.Engine.dispatch(%EnrollLearner{user_id: u, course_id: c}) {:noreply, socket} end # the engine never changes; only the caller does
orthogonality & ETC
The part that changes (the web) is kept separate from the part that stays (the Portal).
one seam
Both front ends call dispatch/1; F6 is a swap, not a rewrite.
That closes F5.01: the Portal runs, the roadmap is set, and the web is replaceable. The next module, F5.02 — Modeling the Portal domain, gives the engine its first real shape. Back to the module overview or the chapter.
References
Primary sources for this lesson, and where it connects in the course.
Sources
- Introduction to Mix — Elixir documentation — applications, deps, and tasks.
Mix— Elixir documentation — the build tool reference.