# The graph model and document

A diagram is a model of nodes, ports, links, and groups; save that model as a document, then reconstruct it when you load the document.

## One diagram, two layers

The [`DiagramModel`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-models-diagrammodel#diagrammodel) holds diagram data: nodes, links, groups, and viewport state. The engine owns behavior such as commands, history, validation, and layout. Framework bindings accept convenient specs and turn them into live models; a rendered instance gives you access to the model behind its canvas.

The [`NodeModel`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-models-nodemodel#nodemodel) represents an item with an identity, type, geometry, data, and ports. A node starts with four bidirectional ports, one on each side. Add a [`PortModel`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-models-portmodel#portmodel) when a connection needs a specific direction, side, or other port-level configuration. Ports describe where a connection may attach and what constraints apply; they are not separate diagram nodes.

A [`LinkModel`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-models-linkmodel#linkmodel) connects source and target ports. Its path type records the geometry intent; the renderer draws and routes the line. A [`GroupModel`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-models-groupmodel#groupmodel) collects member entities into a container and can itself be nested in another group.

```mermaid
flowchart LR
  Diagram["DiagramModel"] --> Nodes["NodeModel"]
  Nodes --> Ports["PortModel"]
  Links["LinkModel"] --> Ports
  Diagram --> Links
  Diagram --> Groups["GroupModel"]
  Groups --> Nodes
```

## Mount a diagram and round-trip its document

Use [`render`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-element-core#render) to mount the data spec and get a live instance. Read the model from that instance, serialize it with [`DiagramSerializer`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-engine-serialization#diagramserializer), then pass the saved JSON to [`fromDocument`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-element-core#fromdocument) to reconstruct the model.

This browser example renders an order flow, serializes it, and reconstructs its model. Give the canvas a height so it has room to draw.

```html
<div id="original" style="height: 360px"></div>
```

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

const originalHost = document.getElementById('original')!;
originalHost.style.height = '360px';

const original = render(
  {
    nodes: [
      {
        id: 'intake',
        type: 'rect',
        position: { x: 40, y: 70 },
        size: { width: 130, height: 56 },
        label: 'Intake',
        data: { owner: 'Operations' },
      },
      {
        id: 'review',
        type: 'rect',
        position: { x: 250, y: 70 },
        size: { width: 130, height: 56 },
        label: 'Review',
        data: { owner: 'Finance' },
      },
    ],
    edges: [{ id: 'intake-review', source: 'intake', target: 'review' }],
    groups: [
      {
        id: 'approval',
        label: 'Approval',
        children: ['intake', 'review'],
        bounds: { x: 20, y: 30, width: 380, height: 150 },
      },
    ],
  },
  originalHost
);

const diagramModel = original.getModel();
const serializer = new DiagramSerializer();
const savedDocument = JSON.stringify(serializer.serializeEnvelope(diagramModel));
const restoredModel = fromDocument(savedDocument).model;
console.log('Restored node IDs:', restoredModel.getNodes().map((node) => node.id));
```

The canvas shows two labelled nodes joined by an edge inside the Approval group. The saved envelope contains the diagram model data, including nodes, links, groups, and viewport. `fromDocument()` accepts the JSON string and returns a loaded spec containing the restored model; the console lists its node IDs.

## What the document contains

Serialization gives persistence one diagram-level format rather than separate node, edge, and group files. The flat model serialization includes a schema version, identity and metadata, the diagram name, nodes, links, groups, and viewport; each node entry includes its serialized ports. Strokes and comments are included when present. `serializeEnvelope()` wraps the diagram data with portable document metadata. `deserialize()` accepts the envelope as well as the flat serializer form, while `fromDocument()` turns a saved document into a renderable spec.

The document preserves model data and connection intent, not functions or an application's runtime. A custom-node painter is code and must be supplied again when loading a diagram that needs it. The renderer remains responsible for drawing and routing from the restored model.

## Where to go next

- [Configure ports](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/configure-ports) for port direction, positioning, and connection options.
- [Route and edit edges](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/route-and-edit-edges) for link geometry and routing.
- [Group nodes and containers](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/group-nodes-and-containers) for group membership and nesting.
- [Import and round-trip diagrams](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/import-and-round-trip-diagrams) for persistence and text formats.
