Sprotty
Toggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Overview

The base architecture of Sprotty revolves around an unidirectional cyclic flow of information between three major components: the ActionDispatcher, the CommandStack, and the Viewer. This leads to a clear and easily testable flow of data which prevents feedback loops.

flowchart TD;
ActionDispatcher
CommandStack
Viewer
ActionDispatcher -->|Command| CommandStack
CommandStack -->|SModel| Viewer
Viewer -->|Action| ActionDispatcher

Action Dispatcher

The main role of the ActionDispatcher is to receive an Action and produce a corresponding command to be transmitted to the CommandStack using ActionHandlers. All operations on the diagram must be passed through the ActionDispatcher, so the CommandStack and the Viewer must never be invoked directly.

The ActionDispatcher also communicates with the ModelSource through Actions in a bidirectional manner, for example to inject external data into the loop or apply edits to the ModelSource.

flowchart LR;
ModelSource
ActionDispatcher
ModelSource -->|Action| ActionDispatcher
ActionDispatcher -->|Action| ModelSource

Action

Actions are objects without behavior, JSON structures that describe what should happen but not how it should happen. As such, they can be serialized and serve as protocol messages that are exchanged between the client and the server. In actions, model elements are referred to by their IDs.

Model Source

There are two different ModelSources: the LocalModelSource offers an API to control the model directly in the client, while the DiagramServer delegates to a remote source, e.g. through a WebSocket or a VSCode extension.

Command Stack

The CommandStack executes the commands it receives from the ActionDispatcher. It chains the promises returned by the execution methods and keeps an undo and a redo stack. It merges the current commands with the last one, e.g. to only keep the start and end point of a move from a drag operation. It is responsible for producing a graph model (namely SModel) and forwards it to the Viewer to be rendered.

Command

Commands describe the behavior of their corresponding Action. They have the typical methods execute(), undo()and redo(), each of which take the current model and a command execution context as parameter, and return the new model or a promise for it. The latter serves to chain asynchronous commands such as animations.

SModel (SprottyModel)

The diagram is stored in an internal model called SModel. The root of the diagram is always an instance of SModelRootImpl and holds an index of the model to allow fast lookup of elements by ID. All elements of a diagram inherit SModelElementImpl which has a unique ID and a mandatory type referring to its View. The model elements are organized in a tree derived from the children and parent properties of each model element. It can be useful to introduce domain-specific information into the SModel. This can be achieved via creating new element classes that inherit from any related SModelElementImpl.

flowchart BT;
SModelElementImpl
SShapeElementImpl
SEdgeImpl
SNodeImpl
SPortImpl
SLabelImpl
CustomEdge
CustomNode
CustomPort
CustomLabel
SShapeElementImpl --> SModelElementImpl
SEdgeImpl --> SModelElementImpl
CustomEdge -.-> SEdgeImpl
SNodeImpl --> SShapeElementImpl
CustomNode -.-> SNodeImpl
SPortImpl --> SShapeElementImpl
CustomPort -.-> SPortImpl
SLabelImpl --> SShapeElementImpl
CustomLabel -.-> SLabelImpl

Viewer

The Viewer is responsible for turning the internal model into its representation in the DOM. The conversion from an SModel to its representation in the DOM is not direct. Instead, Sprotty first creates a VirtualDOM and uses it to patch the actual DOM. This approach saves on expensive modification of the DOM by applying only the minimum amount of modification to it. The Viewer receives an SModel from the CommandStack and traverses it to apply a corresponding View to every element. The viewer is also responsible to add event listeners and animations using its Decorators. The received events should be converted to Actions and transferred to the ActionDispatcher.

flowchart LR;
Viewer
ViewRegistry
Views
VirtualDOM
DOM
Viewer --> ViewRegistry
ViewRegistry --> Views
Views -->|render| VirtualDOM
VirtualDOM -->|patch| DOM
DOM -->|event| Viewer

View Registry

The Viewer uses the ViewRegistry to look up the View for a graph model element using its ID.

View

A View knows how to turn a graph model element and its children into a virtual DOM node. It uses JSX technology and contains a render method producing one or a group of SVG elements.