# Model and document

Grafloria keeps one diagram model beneath every framework binding: nodes, links,
ports, groups, and viewport data are the persistent meaning of the diagram;
rendering and layout turn that meaning into geometry on screen.

## How the parts fit together

```mermaid
flowchart LR
  S["Node and edge specs"] --> I["DiagramInstance"]
  I --> M["DiagramModel"]
  M --> N["nodes"]
  M --> L["links and ports"]
  M --> G["groups"]
  M --> D["serialized document"]
  D --> F["fromDocument()"]
  F --> I
```

The framework component supplies initial or controlled specs. The mounted
[`DiagramInstance`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-renderer-instance-diagraminstance#diagraminstance)
owns the live diagram and gives you the model through `getModel()`. Use that
model for queries and persistence; use `getEngine()` for behavior such as
layout, validation, and undo.

## Nodes identify the things in the diagram

A [`NodeModel`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-models-nodemodel#nodemodel) has an ID, a
type, geometry, ports, style, and data. The ID is the stable identity used by
links and groups. Keep it when you update a node: reconciliation can then
update the existing live model instead of treating the item as a new one.

At the spec boundary, a node can carry `id`, `position`, `size`, `label`,
`data`, and `ports`. The four default ports are deterministic; add explicit
ports when a connection must use a particular direction or port identity.
The model exposes those ports for lookup, including `getPortBySide()` and
`getPort()`.

## Links express relationships, not pixels

An [`EdgeSpec`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-renderer-instance-edgespec#edgespec) names its
endpoints with `source` and `target`. Those values are node IDs. Set
`sourceHandle` or `targetHandle` to pin an endpoint to a port; a bare side such
as `'right'` is also accepted. Without a handle, the renderer chooses the
appropriate port-facing attachment as the nodes move.

`type`, `router`, `connector`, labels, styles, and waypoints describe link
intent. The model stores that intent, while routing and rendering calculate the
visible path. Do not persist a screen-space path as a substitute for the link's
endpoints.

## Groups add membership and containment

A [`GroupSpec`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-renderer-instance#groupspec) is a zone with an
ID, an optional label, and child IDs. Its `bounds` can fix the frame; otherwise
the frame fits its children using `padding`. Children become group members and
move with the group. Groups can be nested and collapsed, so they are model
entities rather than a background rectangle.

The live [`GroupModel`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-models-groupmodel#groupmodel)
stores membership, collapse state, geometry, and layout configuration. Removing
a group through `setGroups()` keeps its boxes; it removes the zone, not the
nodes inside it.

## One document is the persistence boundary

The [`DiagramModel`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-models-diagrammodel#diagrammodel)
contains maps of nodes, links, and groups. Serialize that live model rather
than only the framework's input arrays: gestures and model-level details such
as ports, groups, metadata, and waypoints belong to the live model.

[`DiagramSerializer`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-serialization#diagramserializer)
turns the model into a plain serialized object. `serializeEnvelope()` wraps it
in the portable document envelope when you need generator identity and related
document metadata. The document is data; renderer callbacks and other
post-render wiring are functions, so they are not in the file and must be
supplied again when a host reloads a custom or kit-based diagram.

## Save a mounted diagram and mount it again

This browser example creates two visible nodes and one link, saves the first
instance's model, and mounts a second instance from the saved JSON. Both hosts
have dimensions, so both diagrams paint.

```js
import { fromDocument, render } from '@grafloria/element';
import { DiagramSerializer } from '@grafloria/engine';

const nodes = [
  {
    id: 'intake',
    type: 'rect',
    position: { x: 40, y: 60 },
    size: { width: 120, height: 48 },
    label: 'Intake',
  },
  {
    id: 'review',
    type: 'rect',
    position: { x: 260, y: 60 },
    size: { width: 120, height: 48 },
    label: 'Review',
  },
];

const edges = [
  { id: 'intake-to-review', source: 'intake', target: 'review', label: 'checks' },
];

const firstHost = document.getElementById('first-diagram');
const secondHost = document.getElementById('second-diagram');

if (!firstHost || !secondHost) {
  throw new Error('Both diagram hosts are required');
}

firstHost.style.height = '400px';
secondHost.style.height = '400px';

const first = render({ nodes, edges }, firstHost);
const saved = JSON.stringify(new DiagramSerializer().serialize(first.getModel()));
const loaded = fromDocument(saved);
const second = render({ nodes: [], edges: [] }, secondHost);
second.setNodes(loaded.nodes);
second.setEdges(loaded.edges);

first.renderNow();
second.renderNow();
```

The first call returns the live instance. The serializer reads its model, and
`fromDocument()` converts the saved object or JSON string back into a spec that
[`render`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-element-core#render) can mount. The second canvas
therefore receives the saved node IDs, link endpoints, labels, and geometry.
Provide any application-owned custom-node painter or kit wiring again when
loading a document; those callbacks are not serializable.

Use the instance for the rendered surface and its model for document data. If
you only project nodes and edges back into plain specs, details outside that
projection can be lost. Use the full document path when ports, groups, metadata,
or edited waypoints must survive.

## Related

- [Save and restore diagrams](https://atloria.dev/p/grafloria-h7YM7amryF/developer/save-and-restore-diagrams)
- [Ports and validation](https://atloria.dev/p/grafloria-h7YM7amryF/developer/ports-and-validation)
- [Group nested diagrams](https://atloria.dev/p/grafloria-h7YM7amryF/developer/group-nested-diagrams)
- [Commands, events, and undo](https://atloria.dev/p/grafloria-h7YM7amryF/developer/commands-events-and-undo)
- [How Grafloria works](https://atloria.dev/p/grafloria-h7YM7amryF/developer/how-grafloria-works)
