Understanding Your Model
You author by conversation, but you still own the result — and you should be able to read it without the agent. Every model produces two committed files:
model.modelith.yaml— the canonical source. Self-describing (kind+version), it's what the agent edits and what CI validates.model.modelith.md— the rendered Markdown (with an embedded Mermaid diagram). This is the easiest-to-read form; people and agents read it directly, and CI fails if it drifts from the YAML.
Commit both. The Markdown is generated from the YAML — never hand-edit it; change the YAML (via the agent) and re-render.
The four sections
A *.modelith.yaml file has four top-level sections:
glossary— ubiquitous-language terms that aren't entities (roles likeOwner, states, domain nouns), each with a definition.enums— first-class enumerated types, referenced by an attribute'stype.entities— the named concepts, each with a definition, relationships, attributes, actions, and invariants (each invariant carries a stableid).scenarios— short narratives that exercise the entities to stress-test whether the model hangs together. Scenarios render as formatted text steps today;sequenceDiagramrendering is a roadmap item.
A fifth, optional top-level section — invariants — holds rules that span
several entities and have no natural single owner (e.g. "when a Project is
archived, none of its Policies remain enabled"). It uses the same
{id, statement} shape as entity invariants and shares their id namespace; reach
for it only when a rule genuinely has no single home (see the
Schema Reference).
A minimal model looks like this:
# yaml-language-server: $schema=https://modelith.sh/schema/domain-model/v1.json
kind: DomainModel
version: v1
title: My Product
glossary:
Owner: "A `User` with full control of a `Project`."
Member: "A `User` with access to a `Project` but no ownership rights."
entities:
Project:
definition: >
A container owned by at least one `User`.
relationships:
- entity: User
cardinality: "n:n"
role: "`Owner` or `Member`"
invariants:
- id: at-least-one-owner
statement: "Must have at least one `Owner` at all times"
User:
definition: A human principal who owns or belongs to `Projects`.
invariants:
- id: unique-email
statement: "Email address is unique across all `Users`"
scenarios:
- name: Create a project
actors: [User]
steps:
- "A `User` creates a `Project` and becomes its `Owner`"
invariants_touched: [at-least-one-owner]
See the Schema Reference for every field, and Reading the Diagrams for how the rendered ER diagram works.
The backtick convention
In freeform text (definitions, steps, invariants), entity names are wrapped in
backticks — `Project` — so the renderer formats them as code and the
linter can check they reference real entities. In structured fields that already
imply an entity (actors, relationship entity:, entity keys), the backticks
are skipped. The agent follows this automatically; it's worth recognizing when
you read the YAML.