Skip to content

Architecture Rules ​

Universal MVT constraints. These apply to any MVT implementation regardless of language, renderer, or framework. For this project's code conventions, see the Style Guide.

Related: Architecture Overview - Models - Views - Bindings - The Ticker


Model Rules ​

RuleConstraintDetail
M-timeAll state advances through update(deltaMs) onlyNo wall-clock time, no auto-advancing timers. Time enters exclusively through the ticker's deltaMs. Guarantees determinism: same inputs, same outputs.
M-isolationModels must not reference views or the tickerModels are self-contained simulations. They must not import or know about any view, presentation technology, or the ticker itself. Enables independent testing and renderer substitution.
M-domainState uses domain-level terms, not presentation termsPositions in tiles, metres, or world-units - not pixels. States as named values ('alive', 'exploding') - not colours or texture names. The model defines the world; the view decides how to draw it.
M-compositionParent models delegate update(deltaMs) to childrenCross-model concerns (collisions, phase transitions) live in the parent, after children have updated. Each child is independently testable.

View Rules ​

RuleConstraintDetail
V-statelessViews hold no domain stateRead state and write presentation output. No domain logic, no autonomous behaviour. Views are projections that can be replaced without affecting simulation outcomes.
V-refreshrefresh() runs once per frame, after all models have updatedViews read settled state - no view sees a half-updated world.
V-idempotentrefresh() must be idempotentCalling it twice with the same model state produces the same output. No hidden side effects accumulate across frames.
V-readonlyrefresh() must not mutate modelsViews report user input through relay bindings; they do not act on it in refresh. Views are read-only projections within the frame.
V-reactiveQuery bindings given as functions must be re-read in refresh(), never cached at constructionTheir values may change between frames, and the view must always reflect current state. A query binding the view reads only once must be declared as a fixed value, so the bindings state the limitation. See Fixed and Changeable Binding Values.
V-presentationViews may hold cosmetic presentation state the model doesn't trackSuch views gain an update(deltaMs) method. It advances presentation state and writes no presentation output: refresh() writes all of it, including adding and removing elements. Presentation state starts valid at construction, since a view's first refresh() may come before its first update(). Extract complex logic into a view model. Presentation state must not affect domain outcomes.
V-outputViews can target any output technologyCanvas, DOM, audio, terminal, test harness. MVT is not coupled to a particular renderer.
V-treeView trees do not need to mirror model treesDomain structure and presentation needs are different concerns. Bindings decouple the two hierarchies.

Ticker Rules ​

RuleConstraintDetail
T-sequenceEach frame follows strict order: update models, advance view state, refresh views, renderNever interleave or skip steps. Models settle before views read.
T-capCap deltaMs to a safe maximumPrevents large time gaps (from lost focus, loading stalls, slow devices) from overwhelming models that assume small inter-frame deltas.
T-minimalThe ticker contains no domain logic and no rendering codeIt is purely a timing orchestrator.
T-controlThe ticker may pause, slow, speed up, or single-step timeModels stay in sync because they only see deltaMs.

Binding Rules ​

RuleConstraintDetail
B-contractBindings are the contract between a view and the worldQuery bindings read state (model to view). Relay bindings report user input out of the view (view to model). The bindings type is a complete manifest of every dependency.
B-reusableReusable leaf views use a bindings interface; top-level views may access models directlyLeaf views stay decoupled and reusable. Top-level views are application-specific.
B-optionalRelay bindings should usually be optionalKeeps views usable in more contexts without forcing no-op handlers.
B-wiringBindings are wired at the view construction siteThe view does not know how it is connected to the outside.

Hot-Path Rules ​

RuleConstraintDetail
H-costMinimise per-tick computation costPrefer O(1) lookups over repeated traversals. Cache derived values.
H-allocAvoid per-tick heap allocations in update() and refresh()Minimises garbage collection pressure.
H-loopsUse index-based loops and pre-allocated structuresAvoids iterator and temporary array allocation.
H-changeUse change detection for discrete state that changes rarely but triggers expensive workCheck every frame, rebuild only on change.