-
Notifications
You must be signed in to change notification settings - Fork 85
Architectural Overview
The base architecture is inspired by Flux and other reactive web frameworks. The key feature is a unidirectional cyclic event flow. As opposed to the classical model-view-controller pattern, the event flow is always clear, will not form feedback loops and should be a lot easier to test.

The diagram is stored in a model called SModel. All elements inherit from SModelElement. An SModelElement has a unique string ID and a type to look up its View. The model elements are organized in a tree, induced by the properties parent and children. The root of is always an instance of SModelRoot, which holds an index of the model to allow fast lookup of elements by ID.
As sprotty is also suitable for a client/server scenario, the graph model has to be serializable. The serialized JSON type of an SModelElement is called its schema. In the schema, cross-references are represented by IDs of the referred element. For example, an SEdgeSchema refers to its source using the id of its source SNode. Deserializing a schema to create the corresponding model is the task of an SModelFactory.
SModelExtensions are interfaces that describe additional data of an SModelElement that is needed for some Feature, e.g.
-
Selectable: An element can be selected. -
Moveable: An element can be moved. -
BoundsAware: An element with a width and height that can be updated. -
Scrollable: An element that stores an additonalPointthat describes its scrolling state. -
Zoomable: An element that can be zoomed in and out. Zoom state is described by a number. -
Viewport: AnSModelRootthat is both,ZoomableandScrollable.
The base architecture of sprotty does not presume that the visualization is a graph with nodes and edges. As this is a common case, we have added the graph abstractions as a library. An SGraphcontains SNodes connected by SEdges. SLabels denote some text in the diagram, e.g. inside a node. SCompartments stand for an area within a node that shows a couple of SLabels, like the attribute compartment in the node for a class in a class diagram.
Actions describe certain operations on the graph model. As plain JSON objects they can be serialized. They are the protocol messages that are exchanged between client and server. In actions, model elements are referred to by ID.
The ActionDispatcher receives actions either from the Viewer or from a ModelSource. It converts them to commands using ActionHandlers and passes them to the CommandStack. All operations on the diagram must be passed through the ActionDispatcher, so the CommandStack and the Viewer must never be invoked directly.
An IActionHandler takes an action and either converts it to a command, thus adding the behavior, or forwards it to another component. The ModelSource is also an implementation of IActionHandler and is the entry point for the model to be visualized.
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.
Commands correspond to Actions and describe the actual behavior of the operation. 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, e.g. animations.
Animated commands use an Animation to report updates to the Viewer on every rendering pass, during duration milliseconds from the start of the execution, before they resolve their promise. An example is the MoveCommand, which smoothly interpolates the positions between start and end position.
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 by drag operation. Once the new graph model is available, it is forwarded to the Viewer.
The Viewer creates a virtual DOM from the new graph model and uses it to patch the current DOM. The viewer is also responsible to add event listeners and animations using its Decorators.
A View knows how to turn a graph model element and its children into a virtual DOM node.
The Viewer uses the ViewRegistryto look up the View for a graph model element using its ID.
VNodeDecorators are applied by the Viewer to the View elements, e.g. in order to register event listeners and animations.
Sprotty collects events from the DOM centrally in the MouseTool or KeyTool. These postprocessors dispatch the events to their registered Listeners, which usually belong to some Feature. Listeners are registered using dependency injection.
A Feature usually stands for some sort of interaction of the user with the diagram. It typically consists of
- A singleton
Symbolas an identifier. The methodSModelElement#hasFeature(feature: Symbol)should return true if the model element supports this feature. - An
SModelExtensionto store additional information in the model to support the feature, i.e. the (x,y)-position of a moveable element. -
Actionsto trigger the interaction. -
Commandsto execute the interaction. -
Listenersthat listen to the events that trigger the interaction on the DOM elements. - A
Moduleto register all the above to the client infrastructure (see Dependency Injection).