nSpace® might be classified as a member of the flow-based programming (FBP) genre but due to its inherent structure it is more like some connection-oriented programming paradigms.
Like FBP the basic idea of nSpace is that information ‘flows’, facilitated by the nSpace engine, through an interconnected set of graphs that perform some predefined operations on the data. A behavior specific piece of functionality implemented in a high-level object-oriented language is instanced and connected to other behaviors.
This collection of instanced graph definitions along with their embodied behaviors are located in a computational fabric called a ‘namespace’
which is organized in system memory – or through state mirroring – distributed across the memory of a collection of systems over the network. The namespace is a hierarchical fractal structure which is dynamically created by the instancing of graphs by the nSpace engine and it is this structure that provides deterministic behavior while allowing for emergence of new behaviors by the running system.
The actual code of the application is minimized to the instructions needed to interface to hardware via the operating system and execute the application on a computing device. The applications’s domain ‘knowledge’ (context) is implemented in such a way as to allow it to be portable across multiple computing platforms
from mobile to cloud computing utilizing various hardware, operating systems and network architectures.
The key is that the (IP) Intellectual Property which is the method/process or specific domain knowledge is not embedded in the code but is defined at a higher level of abstraction separating the context from the underlying, environment specific implementations of required functionality (code). In large scale software applications this leads to the construction of fully extensible, functionally portable, relatively efficient and robust software.
Since all of the ‘knowledge’ of an application is described in the form of graphs, portability becomes fairly simple. Only the underlying nSpace engine and behaviors need to be ported to a new language or operating system. All of the graphs that have been tested and running in completed applications will immediately be available in the new environment, provided the behaviors can be made to perform the same function in the that environment.
As with security and portability, reliability is enhanced as well due to all of the knowledge existing in graphs and a very limited code base implemented as behaviors. Adding new capability to a system in the form of graphs immediately inherits the reliability of previously tested and running graphs.
Sometimes interfacing to a legacy computer system or device can be an unavoidable messy business. One of the nice side effects of using nSpace is that messiness is ‘contained’ either within the graph that deals with the issues or just a specific behavior. The new code is implemented in a behavior and is ‘handed’ messages/values from the running graph in the namespace. The behavior takes the appropriate action and ’emits’ the proper output back into the graph. This makes the code segments very concise and simple.
Since behaviors have an existence independent of any graph, they can also be ‘unit’ tested a lot easier than a huge library of code. Although code libraries can be broken up into testable units, generally the code for a particular high-level solution cannot because of the interdependencies, only libraries have a definite ‘beginning’ and ‘end’ to their functionality that can easily be tested. Since nSpace is based on such a small amount of code (contained in functional behaviors) the level of modularity achieved is maximized.
Software security can be compromised through many different means but one common method is through exploiting coding errors such as buffer overruns. From that perspective an equivalent nSpace solution will inherently be more secure since the code base is so much smaller than a full code-based solution. Simply having a smaller attack vector is an advantage. Also, the only time new code is introduced is when a new behavior is required. Many solutions can be created with existing graphs without the need for more behaviors so brand new ‘programs’ can be created that are just as secure as previous ones. Security from a system level is also enhanced. Instead of requiring an ‘independent’ development effort to write additional code to implement security, the operating namespace can be secured independent of the application’s graphs similar to setting the permissions on directories and files in a file system.
In nSpace, security can be handled on a global, independent layer without interfering with currently running solutions. As stated before, running graphs are placed in a namespace appropriate to whatever problem they are solving. These namespaces can run independent of each other and access to their internal paths can be allowed or denied through a simple security mechanism placed in front of the namespace. In other words, if an external entity wants to inspect a value or connect to a graph from another namespace, the security ‘layer’ can service that request through its own filtering. This allows entire security mechanisms to be swapped out or enhanced without effecting the environment of the running application.
With nSpace, the developer no longer has to worry about creating multiple threads of execution and synchronizing data between those threads to prevent race conditions, etc. Threads are created ‘on demand’ implied by the use of certain behavior functionality (such as timers or socket I/O availability and detection). In some environments ‘information’ needs to be assigned different levels of priority based on what the information contains. Since only threads can be assigned a priority, a different layer on top of the thread layer must be implemented to handle this prioritization. Since nSpace is based on the flow of information between graphs it opens up the opportunity to handle both the above issues in a simpler fashion.
This flow allows nSpace to create threads or whatever equivalent structure is needed for the environment (i.e. message passing) without the developer having to worry about the details or capabilities of the underlying operating system. In the case of designing a graph one focuses on the problem to be solved rather than the implementation details. If threads are needed by the individual behavior to fulfill the requirements of the graph they are created automatically. A simple example is a timer that emits signals at some predetermined rate.
A more sophisticated example is an asynchronous queue where values are received on one thread and emitted on another. When designing the graph that has such a queue one just specifies the connections and the asynchronous nature is taken care of by the operating system. When it comes to prioritization of information, ‘paths’ can be elevated (and thus the information flowing through the path) in priority rather than having to worry about the mechanics of how that is achieved.
nSpace necessarily promotes more re-use of solutions since the task is no longer finding a ‘library’ that is compatible to the current language and operating system the developer can now just find the ‘behavior’ that is required and wire in into the higher level solution. Also the introduction of sub-graphs allow for reuse of common features (i.e. network, I/O, etc.).
A running nSpace application exposes every piece of functionality at the highest and lowest levels. All running graphs can be inspected, recorded, connected to, and rearranged in real-time. This allows a programmer or user to enhance or ‘get at’ functionality not originally intended by the application’s designer. It might be something as simple as a ‘get this counter value’ from deep down in the solution space, or it could be something more complicated like ‘let me know every e-mail address that passes through the application’. A simple ‘load’ to the path to access a location of particular graph in the namespace could open up new avenues of solutions without the original designer having to think about that potential at design time.
The nSpace framework is a different type of implementation. It provides a ‘step up’ in capability and abstraction without imposing any design decisions on the programmer. It is still a ‘clean slate’ when it comes to designing the next application without restricting any potential that a standard language based library might impose. This flexibility has allowed the nSpace engine and concepts to be applied to a number of diverse applications.
nSpace is not a compiled ‘language’ but it still has the level of generality that a meta language provides. Ultimately this generality is what makes nSpace such a diverse environment, although having no ‘built-in’ functionality means it is not a ‘build your application in two steps’ type of environment. However, by creating new and reusing existing graph definitions (i.e. ‘sub-graphs’) one can have a huge ‘library’ of pre-generated solutions from which to start.
Another side effect of nSpace that has emerged is how the idea of ‘persistence’ has changed. In a standard database information is stored in two-dimensional tables. The tables, by themselves, provide no ‘context’ to which an outside observer could reconstruct how the data is used. Where are these relationships specified? These relationships are ‘implied’ by the coded modules that use the data in the tables.
By effectively separating the data from its context, a performance penalty is paid during reconstruction of the context. The reconstruction in this case is the code running on a computer performing the queries and pre-defined operations on the data in the database.
In an ideal situation the data would be stored right along side its contextual representation so that querying (or ‘navigating’) the information could be done in a general way without concern or access to the original ‘code’ that stored the data. In nSpace, the running graphs specify the context of the information that flows through the graphs.
Utilizing the temporal capability of nSpace’s namespace a developer automatically obtains a historical record of changes made to the library of solutions in a running system. This temporal storage is also available to the graphs for storage of any data in the system and is a straightforward way to enable ‘storage’ in an application again without having to write additional logic either in code or in graphs. Data is stored relative to its location within the namespace which also allows easier ‘discovery’ of available data via a file system like browser. The result is ‘previous’ snap-shots in time can be brought up to ‘now’ in order to restore previous solutions and/or data. Query for temporal / history data is available inside graph solutions to order to enable advanced visualization tools or any other logic that requires a historical view of the data.