-
Notifications
You must be signed in to change notification settings - Fork 2
Overview of URSYS Separation of Concerns
WIP - Architecture is being revised
URSYS WebApps are roughly based on the Model-View-ViewModel paradigm (MVVM) with the additional requirement to be framework agnostic. This requires a style of coding that is stricter than "The Front End is the Application" approach.
For example, a "comment manager" manages data, data transformations, and operations related to implementing a comment system.
An URSYS application is implemented so importing a feature module initializes it and exposes its API methods
3. A feature module is made of data, operation, and view transformation submodules that maintain their own state separate from each other.
The three module types are:
- DATACORE - pure and derived data, with persistence and notification services
- APPCORE - app operations, transactions, and feature-wide state management
- VIEWCORE - data transformation to and from user-interface compatible data structures
DataCore modules implement "the model" which consists of both data and an API that manipulates that data.
Conceptually, a DataCore module implements a particular model by splitting it into two parts:
-
Pure Data - the minimum needed to represent data model for application, including persistence and restorage from non-volatile storage.
-
Derived Data - useful data that can be inferred or generated from pure data to make data operations more efficient during runtime. Because they are derived from pure data, there is no need to store it, but it has to be rederived every time the Pure Data changes.
In other words (1) the "pure data" that represents elements of the model and (2) derived data created from the pure data. A typical example is to store a list of "entity objects" that link to each other through their 'id' values, with runtime traversable graph structures that map id to entities, and entities to their linked children.
DataCore modules are designed so they can be imported by both ViewCore and AppCore modules, serving as a centralized place for managing data in a controlled manner. They export an API for performing CRUD operations on the data and notification through its EventMachine interface.
Example Datacore Module Names
dc-inputs - maintains a list of input devices and their current values
dc-project - loads and manages a project datastructure
dc-agents - manages a list of simulation agents
ViewCore modules create the data structures for rendering a user interface aka "View". These data structures are a form of derived data, drawing on DataCore values to convert them into a renderable object in the display library of your choice. This could be a Web-based component in HTML or JSX, or a DisplayList object for a 3D rendering library, or even a simple text report. By consolidating this code inside one or more ViewCore modules, we can keep platform specific code out of our data model, and also support multiple output options through platform-specific ViewCore modules.
Another way of thinking about ViewCore modules is that they are "helpers" that know how to package data in a form that is friendly to a particular display library. They package useful state and export methods to support rendering UI elements with display list objects or specific widgets.
Conceptually, a ViewCore module has two kinds of data:
- View State - runtime data structures that model the non-data aspects of a View such as "modality", "zoom", and so on, scoped to the level of the View itself.
- View Data - a form of derived data that can be constructed from DataCore. Combines both DataCore and View State values to produce view specific data structures. For example, creating an array of objects that could be used for rendering an HTML dropdown, or a cloud of points to draw a graph of provided data.
An an example, consider an HTML Widget that is responsible for displaying a dropdown list. The HTML markup is of this form:
<label>Choose a Entity</label>
<select name="entities" id="entity-select">
<option value="">--Please choose an option--</option>
<option value="ent1">Entity One</option>
<option value="ent2">Entity Two</option>
<option value="ent3">Entity Three</option>
</select>
The DataCore for Entities (dc-entities
) perhaps provides a GetEntities()
method that reports an array of Entity objects that look like:
{ id:'1', name: "Entity One", ...props },
{ id:'2', name: "Entity Two", ...props },
...
The job of ViewCore would be to provide a UI-specific methods GetEntityDropdownList()
that might look like this:
import {GetEntities} from 'dc-entities';
function GetEntityDropdownList() {
const ents = GetEntities();
return ents.map({id,name} => { value:`ent${id}`, text: name });
}
And then your React component could use GetEntityDropdownList()
something like this
import * as VC_ENTITY from 'vc-entities';
function Dropdown() {
const optionData = VC_ENTITY.GetEntityDropdownList();
const options = optionData.map(({value,text}) => <option value={value}>{text}</option>);
return <>
<select name="entities" id="entity-select">
{options}
</select>
</>
}
AppCore modules implement application-level operations. These kinds of operations are distinct from data operations and view manipulations. An AppCore module manages a specific application-level feature, maintaining the runtime data structures that model the operation of the application itself. Another was of thinking of this is that AppCore is like the "Controller" in MVC implementing business logic. In URSYS, the design is perhaps more similar to the "ViewModel" in MVVVM, as it is aware of the kind of user operations that are made accessible through a visual interface that can be clicked and manipulated at will.
AppCore modules exposes "action level" operations as a set of API methods, and provide the means to manage multi-step invocations. It essentially translates between user inputs (as captured by the View system in-use, like React) and converts them into either Data Operations (through DataCore) or changes that affect how the application looks and behaves.
Of all the *Core modules, only AppCore is allowed to bridge the divide been DataCore, ViewCore, and the UI system in use.
Conceptually, there are two parts to an AppCore but the distinction is somewhat blurry:
-
App Data - runtime data structures that hold the "working data" needed to coordinate actions and customize the user experience. This data may be persisted to disk. For example, the "current loaded file", "hidden fields", "game clock" and other information are part of the parameters that are fed as input to various other modules.
-
App State - lower-level runtime data structures that control the appearance and available actions for application's operation, such as global selections, current display mode, etc. Handles operations relating to changing state and keeping track of affected systems.