![Mash block diagram](https://user-images.githubusercontent.com/1248467/66305153-003fda80-e8ff-11e9-9c8c-29f4d94e849b.png)
See also https://github.com/ontola/mash/tree/architecture/docs
Application
An application is expected to bring its own bootstrapping code. This generally contains a way to get the app to the browser (e.g. a html file and JS entrypoint) and all the tooling needed to build the app (node modules, webpack config).
When using link-redux, the entrypoint follows a standard pattern of setting up objects needed by the app (e.g. intl, helmet, stlyling) and mounting via ReactDOM.render
.
This is also when the LinkedRenderStore is set up and configured for usage.
The mash and argu are examples of such applications.
Package
Packages are javascript bundles which can be distributed via various means. When a package is loaded into memory, it can search for window.LRS
which exposes the registerModule
method. This method accepts a ModuleDescription which most importantly contains an array of views and an array of middlewares.
The code to register packages lives in mash.
Views, View APIs
Views are objects with associated metadata about when they should render. When using link-redux, a view object is a 'React Component', though the LinkedRenderStore is written as a generic one.
All the methods which link-redux provides are declarative, meaning that only information on what should be done is given. This too gives additional freedom to make large changes to the system without breaking existing views. E.g. mounting the LinkedResourceContainer
React component with a subject
prop will automatically load the resource and mount the appropriate view.
Though it would be possible for application or package code to call the underlying JS functions directly, views lookups should never be done there, but instead by left to the view frameworks (e.g. link-redux). This has a smaller API surface, making future upgrades to lookup logic easier (e.g. adding tags or roles).
Nitty gritty on view registration
Calling registerAll
on a LinkedRenderStore instance with an array of ComponentRegistration objects will make them available for rendering (e.g. via resourceComponent
etc, though in practice link-redux will manage that for the user).
To make generating the ComponentRegistration objects easier, the static method registerRenderer
and the link-redux register
function have been added, but they all boil down to generating an array of ComponentRegistration objects which can be indexed by the ComponentStore.
Middleware
Though everything could be done in a view, or even declaratively (especially with a server executing the logic), link provides middleware where common logic can be abstracted to.
Tiny example: solid middleware
Medium example: Bookmarks manager
The middleware consists of two parts; the middleware stack and the action creators.
The stack
The middleware stack works like a normal middleware stack, where the event is passed at the top and cascades down each layer until a function resolves to a value. Events are started when calling exec
on the LinkedRenderStore, with the first argument being a NamedNode
of the action to be executed, and a possible second argument for carrying information which can't be (properly) serialized into an IRI or resource.
Each middleware will catch events which they know how to process and execute some logic which (usually) changes state, after which the application will update.
The action creators
Views can execute any action they want by constructing the appropriate resource and requesting the LinkedRenderStore to execute it, but this can become somewhat tiresome. That's why each middleware can provide convenience functions to help views setup and execute those actions.
For example, the createBookmark
action creator takes three arguments (the bookmark list resource, the page to bookmark, and a title). Since this is an easy action (without server logic as well), it serializes the intended action to a single IRI which describes the intended action in full. It then calls the store to dispatch the action.
When the next cycle is ready, the action will be sent through the middleware stack until it hits the create handler in the bookmarks middleware. The middleware calculates a linked delta and asks the system to apply the changes locally, after which it saves the local representation to the pod and resolves the promise with the IRI of the created bookmark.
Ontology
Link-lib contains its own rule based inference engine, the Schema.
Note that even though ontological statements are just data, the LinkedRenderStore exposes a separate method for adding data that should be used to reason with. This was done for performance reasons, mainly to prevent unnecessary view-lookup cache clears when loading a resource. Though this might be enabled by default in a future update.
Sub stores
The layers below the LinkedRenderStore implement most of the logic for the above. The wrapper around rdflib.js, RDFStore tracks changes in the data and is able to process linked-deltas.