TodoMVC presents a simple Todo application template, and challenges the MV* framework authors to implement the missing functionality with their library and then add it to the growing list of options. That’s a cool idea and there’s a lot to be learned by going through the examples and backing source code.
The suggestion was that I should take the challenge and write an ONMjs-based Todo app and bring it to the party. But is ONMjs a model-view-controller (MVC), model-view-view-model (MVVM) or model-view-whatever framework? Or, is it something else entirely?
The short answer is OMNjs is neither a MVC nor MVVM framework. It’s an MSOFS framework. MSOFS?
MSOFS is an acronym that stands for Model-Store-Observe-Feedback-Signal that you have never heard of because I made the moniker up. In spirit MSOFS builds from and derives from the core concepts of MVC but is more granular, and pedantically insists on using the term ‘model‘ in its canonical sense to mean “a schematic description of a system, theory, or phenomenon that accounts for its known or inferred properties and may be used for further study of its characteristics.”
I think in patterns and consider all efforts to codify general truths worthy. But computer scientists need to stop forking the language and work harder, and think more deeply about explaining these truths in terms that don’t force us all to remap the lexicon depending on what we’re reading and who we’re speaking with. Parenthetically there are others who share this pet peeve of mine, but seemingly we’re a small minority.
Let’s break MSOFS down into its constituent parts and discuss each in relation to patterns and concepts that are likely familiar to everyone who made it through the first paragraph of this post.
Model – a data model, or model for short, describes the structure (as in topology) and properties (as in attributes) of a data structure in sufficient detail such that a generic software algorithm can manufacture an instance of the modelled data structure, traverse its hierarchical structure, and ascertain if an instance of some randomly sourced data structure instance conforms to the constraints embodied by the model.
Models in MSOFS do not encapsulate the details of operating on an instance of a data structure as they do in MVC. MSOFS models do not take commands; they are used for reference when constructing, traversing, and validating data. As well, models are the basis of data introspection and addressing – two extremely powerful allies in the never-ending battle to write less, more generic, easily testable code.
Store – a data store, or store for short, stores an instance of a modelled data structure and embodies the generic algorithm mentioned above that leverages the model to automate the construction, initialization, traversal, and validation of the data instance it contains.
You can liken a MSOFS store to a little industrial robot that does what it does according to an external program without any concern or knowledge about what it’s actually doing. A MSOFS store, like a robot, is designed to perform a fixed set of generic (although not necessarily trivial) functions, is specialized to the task at hand by an external program, and is controlled by external operator.
In the ONMjs library implementation of MSOFS, a store is represented by a run-time instance of the generic ONMjs.Store object. ONMjs.Store implements the generic functionality described above, is specialized by a MSOFS model, and controlled externally by your application via its API.
In slightly more detail, ONMjs.Store exposes a simple API that allows you to add, remove, enumerate, access, and introspect the data contained within the store. Additionally, ONMjs.Store’s API provides a sophisticated implementation of the observer pattern that your application leverages to monitor and react to changes in store’s contained data.
Readers familiar with the concepts of inversion of control (IoC) and dependency injection will recognize a lot of similarity between a MSOFS store and an object assembler.
Observe – a MSOFS observer is an application-specific subroutine that is called by an ONMjs.Store instance to notify the subroutine that its contained data has been modified. At a high-level of abstraction writing observer subroutines is very similar to writing an event handler that responds to the occurrence of some semantically meaningful condition (e.g. the addition or removal of an object from the store, the mutation of a property value…) in some application-specific way.
MSOFS is deliberately vague about the specific role of observer subroutines. Unlike MVC that formally defines the responsibilities of controller and view subsystems, MSOFS is agnostic to this distinction. This is not because MVC’s formal notion of view and controller is flawed but rather because MSOFS deals at a lower level of abstraction. This means that a MSOFS observer subroutine may occupy either, both, or neither of these roles.
I am personally fascinated by recursive design patterns that can be re-applied at various levels of abstraction because they afford such great opportunities to write generic and re-usable code. MVC is recursive pattern but in my experience it’s rather difficult to build and maintain hierarchical MVC’s (e.g. the top-level MVC’s model is itself an MVC and so on…) As well, attempting to document and explain the details of such systems is an onerous task.
MSOFS suggests a more granular, lower-level separation of concerns, roles, and responsibilities than MVC that, to my way of thinking, is much simpler to understand and apply recursively .
Feedback – feedback is the act of mutating the data contained within a MSOFS store. The term was chosen because it is typical in systems that leverage the MSOFS pattern that data mutation is affected by MSOFS observer subroutines responding to a data change event. Insofar as data mutation originated the change event in the first place, an observer who reacts by further mutating the data establishes a feedback loop.
Feedback is not solely limited to observer subroutines however. Routines that handle user input, network request completion etc. that mutate the contents of a store are also considered to be part of the feedback loop.
Signal – MSOFS signals are essentially callbacks to observer routines dispatched by a store in response to a data change event. Data change signals differ from data change events in that a change event actuates, typically, more than one signal.
For example, the addition of a new data object instance to a store is considered a change event that initiates multiple signal callbacks. Specifically, adding a new data object to a store dispatches a series of signal callbacks notifying observers of the addition of the object, its constituent namespaces, and the modification of parent objects and their namespaces.
MSOFS in Action
My new ONMjs library is based on the MSOFS pattern and is now available on GitHub (sources, documentation (in progress), and download links).
Edit: Since I wrote this post I’ve received some great feedback alerting me to all kinds of stupid rookie
Additionally, I’ve posted several simple JSFIDDLE projects that demonstrate these concepts in action.
I’m still on the fence about building an official TodoMVC app (we worked out the basic data model and built a functional todo list (minus routing and persistence) in about 5-minutes last night using ONMjs and I sort of lost interest after that because it’s not really a big enough problem to merit all the abstraction). But I should probably do it as a learning exercise (when I’m done with the library docs…)
I mentioned the Model-View-View-Model (MVVM) pattern earlier this article but focused mainly on Model-View-Controller (MVC) in the discussion above because it’s more widely known and more closely related to MSOFS than is MVVM.
As an aside, I’ve spent quite a lot of time this year playing with the fantastic Knockout.js MVVM framework and highly recommend this library for client-side UI work.
The way I’ve been using Knockout.js is rather unique however. Rather than relying on MVVM to manage my data, I leverage MSOFS (via ONMjs) to manage my data and leverage observers to initialize and update my model views, and select view models in response to data change signals.
Using these two patterns together is extremely powerful. In particular ONMjs’ support for data addressing and introspection allows me to author exceptionally flexible and re-usable Knockout.js code.
Comments & Questions?
I would welcome comments and questions on this article but not here on this blog please. If you’re interested in this topic and have questions I’ve set up the ONMjs Google Group for this purpose.