Architecture 101: Building Modeller One Decision at a Time
How Modeller's architecture grows from explicit domain meaning to safe, repeatable generation.
Architecture 101: Building Modeller One Decision at a Time
The core idea
Architecture is not the number of boxes in a diagram. It is the set of decisions that protect the meaning we care about.
For Modeller, the durable thing is the business model: the behaviours a business performs, the facts and rules behind its decisions, and the outcomes those behaviours produce. Parsers, diagram editors, template engines, generated languages, and AI providers can all change. They must not silently change that meaning.
The architecture therefore grows one pressure at a time. Every seam below solves a named problem and introduces a cost. If a seam does not protect domain meaning, make change safer, or enable a required adapter, it has not yet earned its place.
Stage 0: Put behaviour at the centre
An entity-only model describes what data exists but not why the system exists. Modeller begins with behaviour—one complete, externally meaningful thing the system can do—as the organising principle.
An actor participates through a named domain role. A command requests potentially state-changing behaviour; a query requests information without domain effects. An event records a domain-significant fact after it occurs. A workflow is itself a behaviour that coordinates other behaviours toward a domain outcome.
Capabilities group behaviours by enduring business purpose without becoming executable or prescribing the implementation structure.
Pressure introduced: a behaviour needs typed inputs, participating entities, rules, effects, transitions, and events. Those concepts cannot remain prose if tools are expected to reason about them.
Stage 1: Add the semantic model
The semantic model gives those concepts stable, Modeller-owned meaning. Entities provide stable identity and continuity through changes in state. Their lifecycle defines meaningful stages and permitted transitions. Guards decide whether a behaviour or transition is currently allowed. Successful behaviours cause transitions; events record the significant facts that result. Persistence remains outside the domain meaning.
Independently versioned bounded contexts own these concepts and expose explicit semantic surfaces. They federate into one resolved snapshot without requiring separate packages, processes, or deployments.
This is the first deep module: callers work with a small domain vocabulary while the model hides identity resolution, references, type relationships, and invariants.
Pressure introduced: authors need a readable way to create the model, and old definitions need a controlled migration path.
Stage 2: Separate authoring from meaning
The authoring language is an input format, not the model itself. A parser turns source text into the semantic model and reports diagnostics in terms an author can act on. Future YAML, editor, or import adapters can reach the same model without becoming alternative sources of truth.
The persistence decision stores each bounded context as an independently versioned context package. Canonical UTF-8 JSON documents carry semantic meaning; file partitioning, layout, and source provenance do not. Stable IDs survive renames and file moves, while a semantic digest identifies normalized meaning independently of those presentation choices.
Schema versions describe persistence structure; context versions describe compatibility of exported domain meaning. Explicit migrations keep those concerns separate: schema migrations preserve the semantic digest, while model migrations are authored semantic changes. Loaders never silently upgrade packages.
Keeping this seam explicit means syntax can improve without forcing every consumer to understand tokens or syntax trees. It also prevents a convenient exchange format from defining Modeller's semantics by accident.
Pressure introduced: rules contain executable-looking expressions. Their meaning must remain stable across authoring syntax, generated code, and external decision engines.
Stage 3: Own rule-expression semantics
Source expressions compile to a small, versioned, statically typed canonical representation. A bounded, pure reference interpreter defines what those expressions mean. Generated C# and external decision formats are adapters; they must preserve the canonical behaviour.
Rules evaluate typed facts and produce explained conclusions. Decisions compose rules to resolve domain questions, with findings explaining how they arrived at a conclusion.
Policies express choices about what is permitted, required, or entitled. Guards apply rules to whether a behaviour or transition is allowed now; invariants protect every observable state. Behaviours, not any of these rule forms, own effects. This keeps evaluation pure enough to test and explain while leaving state changes in the behaviour that requested them.
The reusable rules runtime
implements the rule evaluation interface.
Its executable contract is documented in the
rules-runtime reference.
It binds a resolved snapshot and deterministic function catalog into an
immutable runtime plan, then
exposes one concurrent Evaluate operation. Requests carry typed facts and
evidence; immutable results
separate conclusions and findings from diagnostics and optional canonical
traces.
Missing information is neither false nor null. It produces an indeterminate result only when the conclusion logically depends on it. Canonical results and traces exclude ambient time, locale, randomness, network state, and operational timing so equivalent evaluations remain structurally equal.
Decision tables execute within the same runtime rather than a separate engine. The initial Unique hit policy rejects overlapping or incomplete tables during binding. Alternate interpreters and generated runtimes implement the same complete interface and pass common conformance fixtures. Deterministic work budgets produce stable failures; host timeouts, process isolation, caching, rendered explanations, and OpenTelemetry are operational concerns that cannot change a semantic result.
The behaviour-governance decision connects that pure evaluation module to domain action through explicit rule bindings. Authorization, requirements, classifications, transition guards, and invariants evaluate before a behaviour commits its outcome, transition, effects, and durable event intents.
Rules explain; behaviours act. No rule evaluation executes effects, and no adapter runs before the final invariant check and atomic commit.
Pressure introduced: users and tools need different views of the same model, including diagrams, documentation, and code.
Stage 4: Treat every view as a projection
A diagram projection is useful, but the position of a box is not domain truth. The semantic model feeds projections for people and generators for machines. No projection is allowed to quietly become a second model.
View definitions select scope, roots, filters, and expansions. Layout state stores geometry separately and can be regenerated or discarded. Documentation can emphasise behaviours and decisions. A generation plan can select the semantic inputs required by a template pack. All remain views over one authority.
The initial diagram set is a behaviour map, lifecycle view, causality and event-flow view, context map, structural view, and rule decision view. Together they expose complementary questions without becoming separate models. The diagram-projection reference shows the executable Child Care lifecycle and rule-decision slices and their stable projection elements.
The diagram editing decision requires explicit typed operations. Moving shapes never changes ownership or relationships, and Remove from view is distinct from Delete from model. The editor-integration reference exposes these same parser, navigation, projection, and edit interfaces to editor protocols while keeping document and session state outside semantic authority.
Pressure introduced: generating files repeatedly is dangerous unless output ownership and overwrite rules are explicit.
Stage 5: Plan before writing
Generation is split into planning, rendering, and writing. Template packs encode a chosen software architecture; they do not own the domain model. The plan makes the intended files and their ownership visible before the filesystem changes.
Generated files carry traceability back to the model and template pack. Files owned by developers remain separate and are not overwritten. This turns regeneration from a risky replacement operation into an ordinary workflow.
Pressure introduced: template engines, target languages, filesystems, and AI providers vary. They need extension points without being able to redefine the core.
Stage 6: Put integrations at the boundary
Adapters translate between Modeller and the outside world. AI may help author, explain, or review a model, but it operates through explicit model operations. It does not become a hidden second implementation of the domain.
This is where dependency inversion becomes concrete: replaceable tools depend on Modeller's contracts. The core does not depend on a particular UI, host language, template engine, persistence product, or AI provider.
The full picture
The complete architecture is a flow from authored intent to verified outputs, with one semantic authority in the middle.
The architecture is intentionally asymmetric. Many authoring and integration paths may enter or leave the system, but all of them pass through Modeller-owned semantics. That narrow waist is what lets the edges evolve without fragmenting the meaning of a model.
The implementation threshold
Architecture is not implementation-ready merely because its interfaces have names. The validation and conformance decision requires executable evidence for every accepted contract before implementation begins, and stronger release evidence before any runtime or adapter ships.
Acceptance scenarios state reviewed Child Care meaning. Conformance fixtures make those expectations machine-readable, while compatibility fixtures retain immutable historical artifacts. Expected observations are authored independently: two runtimes agreeing with one another does not prove that either matches the domain contract.
Semantic validation, evaluation, behaviour governance, projections, persistence, and migration are tested through their public module interfaces. Security, concurrency, cancellation, explanation quality, and canonical trace stability are release gates rather than optional refinements.
Implementation handoff
The accepted architecture is now expressed as a live implementation handoff in the GitHub backlog. That backlog owns sequencing and delivery status; this page keeps only the architectural shape.
Implementation proceeds from executable conformance fixtures to the semantic narrow waist: the canonical model, context persistence and federation, and staged semantic validation. The reusable rules runtime and diagram projections build on that waist. Source and editor authoring follow, then configuration and template packs, generation planning, rendering, safe application, and optional boundary adapters such as AI. The CLI grows incrementally as each capability becomes usable.
Readable Modelling Language compiles through the versioned parsing interface into that same narrow waist; syntax and source layout never become a parallel semantic authority.
The first vertical slice is deliberately semantic. One canonical JSON Child Care package models the ACCS determination application, its lifecycle, the Submit determination application behaviour and outcomes, its transition and rule binding, and the Determine ACCS eligibility rule. The slice resolves and validates an exact snapshot, binds and evaluates the rule with sufficient and missing facts, and verifies its conclusion, findings, missing facts, explanation, and canonical trace.
Readable DSL, editor UI, generation, and application behaviour execution are outside that first slice. This keeps the initial proof focused on the contracts that every later surface depends on.
What is settled, and what is not
This page applies the accepted successor semantic baseline and canonical rule-expression decision. The accepted vocabulary is collected in the ubiquitous language. Architecture pages, model definitions, and implementation interfaces must use those terms consistently. The vocabulary was accepted through Wayfinder issue #15.
The remaining choices are bounded implementation details: exact DSL and language-server affordances, generated-code shape, and physical packaging. They may evolve inside their owning modules but cannot redefine the accepted semantic contracts. A proposed change to those contracts requires a focused architecture issue rather than an incidental implementation decision.
The takeaway
Modeller does not need every adapter, projection, or target language on day one. It does need one authoritative semantic model, explicit ownership of expression meaning, and a generation path that preserves handwritten work.
Defer replaceable choices. Be strict about meaning. Add each new box only when you can name the pressure it resolves.