Skip to content

System Architecture

A systems-level view of Mock Machines: how the code is layered, which way dependencies point, and the output and serving surfaces that wrap the core engine. For the dynamic behaviour — the load pipeline and the simulation loop — see Process Flow; for the type graph, see the Data Model.

Mock Machines is a single Go module. Dependencies point strictly downward: the CLI orchestrates everything; the internal/api (REST) and internal/mcp adapters and the internal/graphviz visualizer wrap the engine through the facade; and the engine (internal/engine) depends only on the standard library and one YAML parser.

Package dependency layers Four layers top to bottom — entry (cmd), adapters (REST API and graphviz), core engine (internal/engine), and foundation (stdlib, yaml). Arrows point downward in the direction of dependency. entry adapters core engine foundation cmd/ — CLI parsing & orchestration main.go → cmd.Execute() REST API + MCP thin adapters over the facade /scenarios/{id}/experiments internal/graphviz state diagrams via Graphviz DOT → SVG · PNG · HTML report internal/engine — core engine no HTTP, no I/O policy; pure simulation logic Loading & schema loader.go · types.go taxonomy.go · distributions.go eval/fsm.go (condition eval) Runtime & scheduling experiment.go · executor.go scheduler.go · planner.go · regime.go timing.go · instances.go Entity storage store.go (EntityStore interface) store_archetype.go columnar struct-of-arrays Observability & I/O eventlog.go · decisionlog.go dataset/import.go · export.go report.go (--analyze) · dump.go (--dump) Go standard library · gopkg.in/yaml.v3 external CLIs invoked on demand: dot (PNG/SVG) · duckdb (optional)
Arrows point in the direction of dependency. The adapters never depend on each other; the engine depends only on the foundation.

Callers (CLI, REST, MCP, tests) only ever touch the facade (mock_machine/), which exposes four nested receivers — Lab → Workbench → Scenario → Experiment. The engine (internal/engine/...) is the implementation; internal/ makes it unreachable from outside the module, so the facade is its only legal consumer.

The engine groups into responsibility-aligned subpackages.

SubpackageResponsibility
model/Schema & domain types — the shape of an entity, its states, distributions, and the declared scheduling regime.
compile/Turns parsed config into a runnable program: load/ (YAML → typed Config), validate/, plan/ (entity order + partitions), diagnostics/ (--analyze, --dump).
runtime/The simulation loop: experiment.go, executor.go, scheduler.go, and eval/fsm.go (condition eval).
store/Entity storage: the EntityStore interface and the columnar store_archetype.go; dataset/ for CSV/Parquet/seed I/O.
telemetry/Observability: eventlog.go, decisionlog.go, timing.go.

An entity is an instance of a state machine — a current state with its typed fields and references. At runtime the live entities of each machine type are stored together in an archetype: one Archetype per machine type, laid out column-wise as a struct-of-arrays. Code addresses an entity by an EntityHandle{machineIdx, row}; field access goes through an ArchetypeView using a precomputed FieldMapping, so reads and writes are O(1) with zero reflection — cache-friendly iteration over thousands of entities per turn.

Concurrency is not declared by the modeller — the planner derives it. At load, BuildExecutionPlan unions machines connected by spawn, message, or reference edges into partitions. Disjoint partitions share no state, so they run independently.

Entry pointUsed byHow it runs
Run(N)Batch CLI, serversThe partition-loop engine. Each partition runs its own N-turn loop with its own clock, cascade queue, future-queue, and log writers; partitions run on separate goroutines and their logs are concatenated.
Step / targeted requestInteractive controlAdvances by exactly one turn; optionally requests one named event on one entity.

Several thin, single-purpose surfaces wrap the engine:

  • REST API & MCP (internal/api, internal/mcp) — sibling adapters over the same facade, exposing scenarios and experiments.
  • Visualization (internal/graphviz) — one state-transition diagram per machine: a .dot per machine, dot -Tpng for --png, and a single embedded-SVG HTML report for --html.
  • Diagnostics--analyze (parallelism verdict, hazards, spawn/message/ reference graphs) and --dump (the fully-resolved config as JSON), both early-exit.
  • Result persistence — one CSV/Parquet table per machine type plus an experiment.json manifest and event_log.jsonl; --duckdb imports them into a single DuckDB database.