-
Notifications
You must be signed in to change notification settings - Fork 1
Understanding Olives' MV* model
Olives is a JavaScript MVC-like framework for building web applications. There are already loads of MVC frameworks out there (more than 30 featured in todomvc.com including labs!) and almost each of them interprets the MVC model in a different manner. To better understand how to develop web applications using Olives, I need to explain its own interpretation first.
Olives' MV* was carefuly designed to address 3 major points:
- Maintanable application
- Extendable application
- User-centred API
Of course there are others, but these 3 certainly had the most impact on Olives' design.
Let's create an extremely simple UI example taken from Olives' dev environment to see what it looks like
When I click on "en" I'd like "Hello Olives!" to be displayed, but when I click on "fr" I want "Bonjour Olives" instead.
In Olives, views are pure HTML, they're completely passive:
<section>
<span data-model="bind: innerHTML, greeting">Default Greeting</span>
Olives!<br />
Choose your greeting :
<a href="#" data-event="listen: click, changeLanguage">en</a>
| <a href="#" data-event="listen: click, changeLanguage">fr</a>
</section>
You'll notice the data- attributes though, they're the only link to the logic. I see multiple benefits to this approach:
- Easy to add behavior to existing html
- Easy to update, change or evolve even by a non programmer
- Easy to style, no pre-processing needed
- Easy to find, the view is in an .html files.
- No new technology to learn, like a specific templating tool
The model is an observable data store provided by the framework. Olives' simplest Store can store data in an Array or an Object, and notify on changes. Of course the Model has no knowledge of both View and Model. Example of an Olives model:
// Initializing the Store with an object that has a greeting key.
var model = new Store({ greeting: "Hello" });
// updating the model...
model.set("greeting", "Bonjour");
// ... will also notify observers
model.watch("updated", function ( key, value ) {
console.log(key, value); // "greeting" "Bonjour"
});
But it can also be sub-typed for persistence, like Olive's LocalStore which stores the data in localStorage, following Liskov principle.
Olives' controller uses the simple Store by default, and it can be overridden by any subtype of Store. This is what happens in our simple demo, which is the reason why no model is explicitly defined.
And then comes the OObject. The OObject is a kind of Controller but with more responsibilities. It's actually a skeleton of the part of a user interface, it's the place that concentrates the whole logic. Let's see it in action.
// OObject creation
var ui = new OObject;
// The behaviors to add to the view can be implemented in plugins
ui.plugins.addAll({
// This is a plugin that links the view to the model.
// Reachable through data-model
"model": new ModelPlugin(ui.model),
// This is a plugin that captures user inputs and
//transmits them to the OObject (ui)
"event": new EventPlugin(ui)
});
// Specify the view of the UI
ui.alive(document.querySelector("section"));
// The function that is called when clicking on "en" or "fr"
ui.changeLanguage = function (event, node) {
// ....
}
The OObject binds the view to the model through the use of plugins. Olives has a built-in plugin called ModelPlugin, which implements double-way binding: when the model is updated, the view gets updated, and vice-versa.
It also has a plugin called EventPlugin which binds the view to methods of the ui, like changeLanguage.
And of course, you can imagine creating your own plugins to extend Olives capabilities. Why not adding mustaches on pictures using a face recognition plugin? Or translating coordinates to localized city names?
The previous example is quite limited. There are two other applications using Olives that serve as example:
TodoMVC This is a more complete application that is divided between 3 UIs that share data.
Suggestions (Under development as of today) Similar to a todo list but with more features, such as routing, real-time model etc.
When looking at the files tree, we see that an Olives application isn't split between several model/controller/view files as we usually have, but in small UIs instead. Each UI is specifically designed for a simple task, like adding a todo, displaying todos, or counting them. And this makes it way easier to design, develop and maintain an application.
While this may not be obvious for a small application with few contributors over a small amount of time, it becomes dramatically more important with bigger projects.
As a project grows in size, its number of files increases, diluting the knowledge of the architecture. As years pass by, developers forget where to put code and just happen to place it where it works. Sometimes duplicating code, data or coupling things that shouldn't be. The cost to avoid this, through processes, also increases as the project ages. I have seen this too often.
On the other hand, Olives MV* has a 1:1 correspondence between the UI and its source code. You could almost see the files tree as you look at the UI. Divide the UI into multiple views, and each view has a single js file. Because of the way the OObject works, it's really hard to mix things up and associate some business logic to the wrong view, giving you even more hints on where to place your code. It's also possible to create generic UIs that can be duplicated throughout the application. The result is that you have a code that is easy to maintain and to extend!
Last but not least, as the example shows, there's almost no plumbing. Scanning through the given code, one can seen that every line has a meaning to the developer and not to the framework. There's no specific Olives way of structuring code, declaring classes, doing inheritance or such, it's just plain JavaScript, and Olives is an API.
That's what I call a user-centered API, but I'll expand my thoughts in another post.