jonnifyagile agents
jonnify / Agile Agent Workflow / A3 · The roadmap layer / A3.6 / A3.6.2 · One core, many surfaces

A3.6 · The program roadmap · dive 2

One core, many surfaces

The Portal is one domain core behind one facade, surfaced through several adapters — a web app, a bot, a future worker, each an adapter over the same facade.

The web (F6) and the bot (F10) are parallel surfaces over that one Portal facade. The master invariant is what makes them independent: every surface calls only the facade and renders only the closed error set. Pick a surface on the right to map it to its adapter, all routed through the one facade.

The core and its surfaces · pick one

Web (F6) LiveView/HEEx Bot (F10) ex_gram, in-BEAM Worker future (reserved) the one Portal facade · every surface calls only this the domain core framework-free · unchanged by any surface
Web (F6) · adapter: LiveView/HEEx · calls only the Portal facade · renders only the closed error set. One core, surfaced — the core is untouched.

One core, surfaced through adapters

The Portal has one domain core, framework-free, behind a single facade. From portal.roadmap.md, verbatim: “There is one domain core, framework-free, behind a single facade (Portal); every surface — a LiveView, a bot handler, a future worker — calls only that facade and renders only the closed %Portal.Error{} set. The program grows by adding surfaces and capabilities over the unchanged core, never by reaching into it.”

A surface is an adapter. The web app (F6) is a LiveView/HEEx adapter; the bot (F10) is an ex_gram handler; a future worker is reserved. Each is its own chapter with its own roadmap, and each sits on the same facade. From the same file: “F6 (web) and F10 (bot) are parallel surfaces over the same F5 facade — neither depends on the other for its core loop.”

The master invariant, verbatim: “The web layer calls only the Portal facade and renders only the closed %Portal.Error{} set. No controller, LiveView, plug, or template names Portal.Engine, a repo, or GenServer.call.” That single rule is what lets a surface be added over the unchanged core.

Add or remove a surface — the core is unchanged

Because every surface calls only the facade, a surface can be added or removed without touching the core. The figure below toggles each surface on or off. However many surfaces are present, the “core changes” counter holds at zero — adding the bot does not change the engine; removing the web app does not change the engine. The master invariant holds throughout.

The two figures teach different moves: the hero maps one surface to its adapter over the shared facade; this one toggles surfaces present or absent and proves the core never changes.

Toggle a surface on or off · the core does not change

web present bot absent worker absent the one Portal facade the domain core core changes: 0
Surfaces present: 1 (web) · core changes: 0 · master invariant: holds. Toggle a surface on or off — the core is unchanged.

A surface is an adapter over the core — add or remove one and the framework-free core does not change.

One core, many surfaces, on the Portal

On the Portal, the core is the F5 engine behind the Portal facade. The surfaces are the web chapter F6 and the bot chapter F10 (with a worker reserved for F7–F9). Each surface is a roadmap of its own that calls only the facade. The companion course F6 · The Portal on the web (Phoenix) is where the framework implements the web surface; this layer only sequences the chapters.

The hand-off is clean precisely because of the master invariant. A new surface adds a roadmap and renders only the closed error set; it invents no domain logic and no new error vocabulary, so the core stays unchanged.

# portal.roadmap.md — how the chapters compose (a sketch; verbatim relations).
#   F5  the engine ──▶  Portal facade  +  closed %Portal.Error{}
#        ├──────────────┬───────────────────────────┐
#        ▼              ▼                           ▼
#   F6 the web      F10 the bot           F7–F9 multi-runtime (open)
#   (LiveView)      (ex_gram)             EchoMQ · workers

# Each surface is an adapter over the unchanged core.
core    = "the F5 engine"      # framework-free, behind one facade
surface = "F6 web | F10 bot"   # adapters; each a roadmap of its own
rule    = "call only the facade"  # the master invariant; the decoupling seam

# Add a surface, add a roadmap; the core never changes.
the principle

One core, many surfaces — a surface is an adapter over the core, added without reaching into it; the core stays the single, framework-free source of behaviour.

on the Portal

“Call only the facade” — the master invariant. The web (F6) and bot (F10) each call only the Portal facade and render only the closed error set, so each is independent of the others and of the core's internals.

The master invariant is the decoupling seam — add a surface, add a roadmap; the core never changes.

Recap — surfaces over one core

The Portal is one domain core behind one facade, surfaced through several adapters. The web (F6) and the bot (F10) are parallel surfaces; a future worker is reserved. Each calls only the facade and renders only the closed error set, so a surface is added or removed without changing the core. The next dive looks at the consequence for delivery: because the surfaces are independent, they ship on independent cadences.

Next in A3.6: A3.6.3 · Independent cadence — each surface ships on its own schedule, sequenced by product priority, not a hard dependency.

References

The decoupling sources behind one-core-many-surfaces, and where it connects in the course.

Sources

Related in this course