jonnifyelixir

F5 · system design · 1 of 3

The Portal engine blueprint

Before the first module, here is the thing the chapter builds. The Portal engine is a framework-free domain core: it knows the Portal’s rules and holds its state, but nothing in it imports Phoenix. It sits in the middle of a stack — the branded CHAMP store from F4 underneath it, the Phoenix LiveView UI from F6 on top — and exposes a single boundary, the facade, that the UI calls. Four layers, and each one is the work of a specific F5 module, so the whole chapter points at one destination: an engine with a clean, UI-ready edge.

Select a layer to see what it does and which module builds it. The engine is the two middle layers; F4 already built the bottom, and F6 will build the top.

The four layers

Reads and writes travel the same stack in opposite directions: a write enters at the top as an event from the UI and settles into the store at the bottom; a read climbs back up as a projection. The engine never reaches past its own boundary — it does not render markup and it does not open sockets — which is exactly what lets F6 mount it without the engine knowing F6 exists.

The stack · select a layer

writes ↓ ↑ reads UI Phoenix LiveView F6 FACADE Portal.Engine — the one public API F5.08 DOMAIN CORE contexts · commands · queries · events F5.02–F5.06 PERSISTENCE branded CHAMP store + Snowflake ids F4 ✓

layer: Engine facade

built by: F5.08

The engine is the two middle layers. Everything F5 does is make those two layers correct and give them a single edge; the layers above and below are someone else’s job, by design.

The shape in code

The boundary is small on purpose: one way to change the system and one way to read it. A LiveView (or a test, or a script) only ever touches these two functions; everything else is private to the engine. That single edge is what F5.08 assembles and what the F5.09 lab mounts behind a LiveView sketch.

# the boundary the UI calls — assembled across F5, mounted in F6
defmodule Portal.Engine do
  # write: a command is checked, emits an event, transitions state (F5.04-F5.06)
  def dispatch(command), do: # {:ok, event} | {:error, reason}

  # read: a projection over the current state (F5.05)
  def query(name, args), do: # {:ok, result}
end

# nothing above imports this; nothing here imports Phoenix — the seam is the point

a framework-free core

The engine knows the rules and holds the state, with no web framework inside it.

one UI-ready edge

dispatch/1 and query/2 — all a LiveView needs.

Next in the design brief: the domain model — the entities and contexts that live inside the core.

References

The ideas behind a framework-free core with one boundary.

Sources

Related in this course