jonnifyelixir
F5 · Pragmatic Programming / F5.09 · the lab

F5 · the engine · the lab

The Portal engine, assembled

Eight modules built the parts. F5.01 stood up a thin server; F5.02 modelled the domain; F5.03 drove a walking skeleton through every layer; F5.04 gave the enroll command a contract; F5.05 split commands from queries and recorded each change as an event; F5.06 put the folded state in a supervised GenServer; F5.07 pinned it with tests; F5.08 drew the boundary — ports, a facade, a closed error contract. This lab is the finale: it assembles them into one running Portal — a supervision tree with the engine behind its facade, fed by an event-store port — mounts it behind a LiveView sketch, and states the handoff to F6. It ships with a spec and copy-paste build prompts, so the same assembly can be generated end to end and verified against a definition of done.

One assembled system, three phases. Select a phase to see what it produces and which dive builds it.

A crash is recovered, not lost

APPLICATION · SUPERVISOR strategy :one_for_one STORE · the log enrolled lesson_done lesson_done ENGINE · running progress: 2/3 state folded from 3 events init replays the log the log outlives the process that folds it

[ running · progress 2/3 ]
State is folded from the 3 events in the store.

The assembled Portal

Top to bottom: a web layer (a LiveView in F6) calls the facade; the facade calls the engine, a GenServer holding the folded state; the engine reaches its event log through the port; an adapter implements the port. The Application supervises the engine and the store, so the running parts come back after a crash. Select a phase to light up the part this lab assembles.

The running stack · select a phase

WEB · LiveView (F6) calls only the facade FACADE · Portal enroll · deliver_lesson · progress_of ENGINE · GenServer decide · evolve · folded state PORT · EventStore append · read_stream ADAPTER · Postgres / InMemory the event log SUPERVISED

phase: Assemble

it produces: the supervised engine, end to end

Nothing in this stack is new; the lab only wires it. The value of eight modules of discipline shows up here, where the parts snap together because each was built to a contract.

Build it with prompts

The lab ships as a spec-first build guide: each phase carries its concepts, a specs table, numbered actionables, and a copy-paste build prompt that already states the spec and the acceptance criteria. Hand a prompt to an agent and it generates the code; check it against the definition of done; move to the next. One representative prompt, the assembly of the supervision tree:

PROMPT — Assemble the supervision tree
In the portal app, wire Portal.Application to start the configured event-store
adapter and the engine under one supervisor, strategy :one_for_one:
- children: [Portal.EventStore.adapter(), {Portal.Engine, []}]
- Portal.Engine.init/1 reads its stream through the EventStore port and folds it
  with replay/1 (F5.05) into the starting state, returning {:ok, state}.
- the facade (Portal) and the Engine.command/query wrappers are the only callers
  of GenServer.call; results pass through Portal.Error.from/1 at the boundary.
Acceptance: the app boots with `mix run`; a fresh enroll persists an event and a
restart of Portal.Engine replays the log to the same state.

The full set — assemble, mount, and handoff, each with its spec and acceptance criteria — ships as the lab build guide alongside this page, so the Portal logic can be generated module by module and verified at each step.

Three deep dives

In order: wiring the supervision tree and the facade so a command runs end to end and a restart replays the log; a LiveView mount sketch that talks only to the facade; and the handoff — what F6 inherits unchanged and what it must not touch.

F5.09.1
The engine facade end to end
The full supervision tree: the Application starts the store and the engine, init reads the stream through the port and replays, and an enroll runs facade → engine → store and persists.
F5.09.2
A LiveView mount sketch
mount/3 loads state via a query, handle_event calls a command and branches on the closed error contract, and render/1 shows assigns — the LiveView touches only the facade.
F5.09.3
What ships in F6
The handoff: F6 replaces the thin web layer with Phoenix and adds its endpoint to the tree, but the facade, the error contract, and the tests carry over unchanged.

F5.08 · a boundary

Ports, a facade, and a closed error contract — the seams are drawn, but nothing is wired together yet.

F5.09 · assembled & mounted

One supervision tree, an engine behind its facade, a LiveView sketch on top — the shape F6 inherits.

Start with the engine facade end to end, then a LiveView mount sketch, then what ships in F6. This lab closes F5; the boundary it mounts is F5.08 · boundaries & integration seams, and the engine it assembles is F5.06 · where engine state lives.

References

The supervision tree, the engine boundary, and the LiveView layer F6 mounts it in.

Sources

Related in this course